http://www.tuicool.com/articles/mQj2AfF
原文? http://lastday.github.io/ioshttplibrary/HttpLibrary2/
主題 iOS開發
一步一步搭建自己的iOS網絡請求庫(二)
大家好,我是LastDay,上一次分享了簡單體驗并且測試下一下NSURLSession。
我的博客地址:http://lastday.github.io
進行簡單的封裝
我們接下來將要加入動態的動態的HTTP參數(parameters)的功能,之后封裝出我們自己的接口。
首先呢,我們先來對上一次的代碼進行一次簡單的封裝,建立一個新的類LyNetWork,繼承于NSObject類 。新建一個靜態的request方法。將請求方式和URL傳入
代碼如下:
+(void)request :(NSString *)method URL:(NSString *)URL{
NSURL *url = [NSURL URLWithString:URL];
NSURLSession *session = [NSURLSession sharedSession];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = method;
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * data,NSURLResponse *reponse,NSError *error){
//NSdata轉String
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",string);
}];
[task resume];
}
OK
讓我們在ViewController中進行一下測試,代碼如下:
@IBAction func mainButtonBeTapped(sender: AnyObject) {
[LyNetWork request:@"GET" URL:@"http://cityuit.sinaapp.com/1.php"];
}
接下來運行一下:OK 依然顯示我們正確的測試結果:succeed
使用Block處理請求返回值
簡單的介紹下閉包(block),對于OC來說是一個新詞,但不是新的概念,不是新的東西。學過Javascript的小伙伴對閉包應該不陌生吧~學過PHP的應該也不陌生,在PHP5.3版本以后也支持閉包, 也就是OC中所提到的Block。
現在對我們的request函數進行修改,LyNetWork.m代碼如下:
+(void)requestMethod:(NSString *)method
URL:(NSString *)URL
success:(void (^)(NSData *__nullable data,NSURLResponse * __nullable response))success
failure:(void (^)(NSError *__nullable error))failure
{
NSURL *url = [NSURL URLWithString:URL];
NSURLSession *session = [NSURLSession sharedSession];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = method;
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * data,NSURLResponse *response,NSError *error){
if (error) {
failure(error);
}else{
if (success) {
success(data,response);
}
}
}];
[task resume];
}
接下來修改ViewController中的函數調用,代碼修改后如下:
- (IBAction)test:(id)sender {
[LyNetWork requestMethod:@"GET"
URL:@"http://cityuit.sinaapp.com/1.php"
success:^(NSData *data,NSURLResponse *response){
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",string);
}
failure:^(NSError *error){
NSLog(@"%@",error);
}];
}
測試結果依然返回succeed,測試結果正確
接下來進行我們最難處理的地方,增加parameters處理能力
我們先來處理GET方法吧
GET 方法下,params 在經過 url encode 之后直接附在 URL 末尾發送給服務器
類似于這個樣子 GET /foo.php?first_name=John&last_name=Doe&action=Submit HTTP/1.1
修改我們的requestMethod方法,將其進行一下更改,引入parameters參數
+(void)requestMethod:(nullable NSString *)method
URL:(nullable NSString *)URL
parameters:(nullable id) parameters
success:(nullable void (^)(NSData *__nullable data,NSURLResponse * __nullable response))success
failure:(nullable void (^)(NSError *__nullable error))failure;
接下來就該處理我們的parameters參數了
我們從AFNetworking中借鑒一下他的處理方案(其實這里本人就是模仿編寫罷了)
我們新建一個類起名為LYURLRequestSerialization,在LYURLRequestSerialization.h添加一下方法
+(NSString *)LYQueryStringFromParameters:(NSDictionary *)parameters;
進入我們的LYURLRequestSerialization.m文件中,添加以下代碼:
#import "LYURLRequestSerialization.h"
@interface LYURLRequestSerialization()
@property (readwrite, nonatomic, strong) id? value;
@property (readwrite, nonatomic, strong) id? field;
@end
@implementation LYURLRequestSerialization
- (id)initWithField:(id)field value:(id)value {
self = [super init];
if (!self) {
return nil;
}
self.field = field;
self.value = value;
return self;
}
#pragma mark -
FOUNDATION_EXPORT NSArray * LYQueryStringPairsFromDictionary(NSDictionary *dictionary);
FOUNDATION_EXPORT NSArray * LYQueryStringPairsFromKeyAndValue(NSString *key, id value);
+(NSString *)LYQueryStringFromParameters:(NSDictionary *)parameters {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (LYURLRequestSerialization *pair in LYQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}
NSArray * LYQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return LYQueryStringPairsFromKeyAndValue(nil, dictionary);
}
NSArray * LYQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:LYQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:LYQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:LYQueryStringPairsFromKeyAndValue(key, obj)];
}
} else {
[mutableQueryStringComponents addObject:[[LYURLRequestSerialization alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}
static NSString * LYPercentEscapedStringFromString(NSString *string) {
static NSString * const kLYCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
static NSString * const kLYCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
[allowedCharacterSet removeCharactersInString:[kLYCharactersGeneralDelimitersToEncode stringByAppendingString:kLYCharactersSubDelimitersToEncode]];
static NSUInteger const batchSize = 50;
NSUInteger index = 0;
NSMutableString *escaped = @"".mutableCopy;
while (index < string.length) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu"
NSUInteger length = MIN(string.length - index, batchSize);
#pragma GCC diagnostic pop
NSRange range = NSMakeRange(index, length);
range = [string rangeOfComposedCharacterSequencesForRange:range];
NSString *substring = [string substringWithRange:range];
NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
[escaped appendString:encoded];
index += range.length;
}
return escaped;
}
- (NSString *)URLEncodedStringValue {
if (!self.value || [self.value isEqual:[NSNull null]]) {
return LYPercentEscapedStringFromString([self.field description]);
} else {
return [NSString stringWithFormat:@"%@=%@", LYPercentEscapedStringFromString([self.field description]), LYPercentEscapedStringFromString([self.value description])];
}
}
@end
簡單的說一下吧,以上的方法都是為了處理傳入的NSDictonary參數,因為我們在使用的時候為了方便我們傳入動態的parameters,所以他的的格式是這樣的:
id parmenters = @{
@"value":@"LastDays",
};
將它處理后我們希望得到的樣式應該是這樣,對吧?
http:URL.php?value=LastDays
這個參數是一個動態的,我們不能確定里面到底有幾組參數,而且還需要考慮的一個問題就是NSDictonary中嵌套NSDictonary的情況,我們處理這種問題的一個思想就是遞歸。從最里面開始處理。
OK,這樣我們就實現了parameters處理能力
然后我們需要測試一下
為了進行測試我又重新更改了接口,提供了參數處理的能力,以下是新的接口:
http://cityuit.sinaapp.com/1.php? value=將要返回的值
更改下requestMethod方法
+(void)requestMethod:(NSString *)method
URL:(NSString *)URL
parameters:(id) parameters
success:(void (^)(NSData *__nullable data,NSURLResponse * __nullable response))success
failure:(void (^)(NSError *__nullable error))failure
{
NSString *newURL;
if ([method isEqual:@"GET"]) {
newURL = [[URL stringByAppendingString:@"?"] stringByAppendingString: [LYURLRequestSerialization LYQueryStringFromParameters:parameters]];
NSLog(@"%@",newURL);
}
NSURL *url = [NSURL URLWithString:newURL];
NSURLSession *session = [NSURLSession sharedSession];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = method;
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * data,NSURLResponse *response,NSError *error){
if (error) {
failure(error);
}else{
if (success) {
success(data,response);
}
}
}];
[task resume];
}
其中變化的地方:
if ([method isEqual:@"GET"]) {
newURL = [[URL stringByAppendingString:@"?"] stringByAppendingString: [LYURLRequestSerialization LYQueryStringFromParameters:parameters]];
NSLog(@"%@",newURL);
}
以上代碼的意思就是判斷一下是否未GET請求,如果是的話將處理后的parameters加到尾部,以剛才的參數為例子,處理后的newURL為:http://cityuit.sinaapp.com/1.php?value=LastDays
也就是說返回值為LastDays
到ViewController.m中進行測試,測試代碼如下:
- (IBAction)test:(id)sender {
id parmenters = @{
@"value":@"LastDays",
};
[LyNetWork requestMethod:@"GET"
URL:@"http://cityuit.sinaapp.com/1.php"
parameters:parmenters
success:^(NSData *data,NSURLResponse *response){
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",string);
}
failure:^(NSError *error){
NSLog(@"%@",error);
}];
}
ok返回結果為LastDays,成功添加入動態的動態的HTTP參數(parameters)的功能。