简述AFNetWorking使用以及二次封装带来的好处

AFNetWorking作为目前使用较为广泛的一个开源的网络框架,它优秀的框架结构和简易的使用方法所带来的方便让很多ios开发者毫不犹豫的选择了使用它。相对于苹果官方自身的NSURLConnection(ios9.0后用NSURLSession取代)来说,官方的网络请求API在使用的时候不如经过AF封装过后的直接使用来得方便与灵活。下面就先来简单介绍一下官方的API的基本使用过程:

IOS 9.0之前

1
2
3
4
5
6
7
8
9
10
11
12
13
一. 同步GET
1.将网址初始化成一个字符串对象
NSString *urlStr = [NSString StringWithFormat:@"%@/attachment/uuid/%@",UUID];
2.如果网址中存在中文,进行URLEncode
NSString *newUrlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
3.构建网络URL对象, NSURL
NSURL *url = [NSURL URLWithString:newUrlStr];
4.创建网络请求
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
5.创建同步链接
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
二.同步POST
1.根据网址初始化OC字符串对象
NSString *urlStr = [NSString stringWithFormat:@"%@", URL];
2.创建NSURL对象
NSURL *url = [NSURL URLWithString:urlStr];
3.创建请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
4.创建参数字符串对象
NSString *parmStr = @"pageIndex=1&pageSize=10";
5.将字符串转为NSData对象
NSData *pramData = [parmStr dataUsingEncoding:NSUTF8StringEncoding];
6.设置请求体
[request setHTTPBody:pramData];
7.设置请求方式
[request setHTTPMethod:@"POST"];
8.创建同步链接
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
1
2
3
4
5
6
7
8
9
10
11
12
三.异步GET
NSString *urlStr = [NSString stringWithFormat:@"http://pic4.nipic.com/20090910/2302530_144753008092_2.jpg"];
NSString *newStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:newStr];
NSURLRequest *requst = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];
异步链接
[NSURLConnection sendAsynchronousRequest:requst queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
self.imageView.image = [UIImage imageWithData:data];
// 解析
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSLog(@"%@", dic);
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
四.异步POST
1.POST请求
NSString *urlString = [NSString stringWithFormat:@"%@",URL];
2.创建url对象
NSURL *url = [NSURL URLWithString:urlString];
3.创建请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
4.创建参数字符串对象
NSString *parmStr = [NSString stringWithFormat:@"pageIndex=1&pageSize=10"];
5.将字符串转换为NSData对象
NSData *data = [parmStr dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:data];
[request setHTTPMethod:@"POST"];
6.创建异步连接
[NSURLConnection connectionWithRequest:request delegate:self];
1
2
3
4
5
6
7
8
9
NSURLConnectionDataDelegate经常使用到的代理方法:
1.服务器接收到请求时
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
2.当收到服务器返回的数据时触发, 返回的可能是资源片段
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
3.当服务器返回所有数据时触发, 数据返回完毕
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
4.请求数据失败时触发
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

IOS 9.0之后

1
2
3
4
5
6
7
8
9
10
11
12
一.GET请求
1.快捷方式获得session对象
NSString *urlStr = [NSString StringWithFormat:@"%@/attachment/uuid/%@",UUID];
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:urlStr];
2.通过URL初始化task,在block内部可以直接对返回的数据进行处理
NSURLSessionTask *task = [session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError error) {
NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
3.启动任务
[task resume];
1
2
3
4
5
6
7
8
9
10
11
二.POST请求
1.创建请求
NSURL *url = [NSURL URLWithString:@"http://pic4.nipic.com/20090910/2302530_144753008092_2.jpg"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=daka&pwd=123" dataUsingEncoding:NSUTF8StringEncoding];
NSURLSession *session = [NSURLSession sharedSession];
2.由于要先对request先行处理,我们通过request初始化task
NSURLSessionTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]); }];
[task resume];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NSURLSessionDataDelegate代理方法
1.使用代理方法需要设置代理,但是session的delegate属性是只读的,要想设置代理只能通过这种方式创建session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self
delegateQueue:[[NSOperationQueue alloc] init]];
2.创建任务(因为要使用代理方法,就不需要block方式的初始化了)
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.loyo.com/login?userName=loyo&pwd=123"]]];
3.启动任务
[task resume];
4.对应的代理方法如下:
a.接收到服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
// 允许处理服务器的响应,才会继续接收服务器返回的数据
completionHandler(NSURLSessionResponseAllow);
}
b.接收到服务器的数据(可能调用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// 处理每次接收的数据
}
c.请求成功或者失败(如果失败,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
// 请求完成,成功或者失败的处理
}

好了前面是简单讲述了一下关于官方的网络请求的调用以及方法的使用,当然,在网络上也有很多这样的介绍,这里就不再详细罗列,下面来讲讲今天的核心内容AFNetWorking,这里主要介绍3.0及之后的使用方法:

注:这里的code码500是代表请求错误的回调码,不一定每个网络请求的错误码都是500,这里只是用来简单举个例子,path是请求的url,params是请求的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
首先将AFHTTPSessionManager 实例化成单例
#import "HttpTool.h"
@interface HttpTool : NSObject
+ (AFHTTPSessionManager *)AFHTTPSessionManager;
@end
static AFHTTPSessionManager *sharedClient;
@implementation HttpTool
+ (AFHTTPSessionManager *)AFHTTPSessionManager {
AFJSONRequestSerializer *jsonRequest = [AFJSONRequestSerializer serializer];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedClient = [AFHTTPSessionManager manager];
sharedClient.responseSerializer = [AFJSONResponseSerializer serializer];
[jsonRequest setValue:[DeviceInfo getSystemName] forHTTPHeaderField:@"Platform"];
[jsonRequest setValue:[DeviceInfo getDeviceModelDetail] forHTTPHeaderField:@"Agent"];
});
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:@"user_token"]) {
NSString *token = [ defaults objectForKey:@"user_token"];
[jsonRequest setValue:[NSString stringWithFormat:@"Bearer %@",token] forHTTPHeaderField:@"Authorization"];//存入token信息
}
[jsonRequest setValue:[DeviceInfo getDeviceVersion] forHTTPHeaderField:@"OSVersion"];
[jsonRequest setValue:[DeviceInfo getVersionName] forHTTPHeaderField:@"VersionName"];
[jsonRequest setValue:[DeviceInfo getVersionCode] forHTTPHeaderField:@"VersionCode"];
jsonRequest.timeoutInterval = 15;
sharedClient.requestSerializer = jsonRequest;
return sharedClient;
}
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1、GET方法
[[HttpTool AFHTTPSessionManager] GET:path parameters:param progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *) task.response;
if (urlResponse.statusCode == 500) {
NSDictionary *successDict = responseObject;
NSString *errorStr = successDict[@"error"];
if (errorStr) {
NSError *error = [NSError errorWithDomain:@"httpError" code:500 userInfo:@{NSLocalizedDescriptionKey: errorStr}];
//回调错误信息
}
else {
NSError *error = [NSError errorWithDomain:@"httpError" code:500 userInfo:@{NSLocalizedDescriptionKey:@"网络请求错误(500)"}];
//回调错误信息
}
}
else {
//回调成功的数据
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (error) {
//回调错误信息
}
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2.POST方法
[[HttpTool AFHTTPSessionManager] POST:path parameters:param progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *) task.response;
if (urlResponse.statusCode == 500) {
NSDictionary *successDict = responseObject;
NSString *errorStr = successDict[@"error"];
if (errorStr) {
NSError *error = [NSError errorWithDomain:@"httpError" code:500 userInfo:@{NSLocalizedDescriptionKey: errorStr}];
//回调错误信息
}
else {
NSError *error = [NSError errorWithDomain:@"httpError" code:500 userInfo:@{NSLocalizedDescriptionKey:@"网络请求错误(500)"}];
//回调错误信息
}
}
else {
//回调成功数据
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (error) {
//回调错误信息
}
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
3.PUT方法
[[HttpTool AFHTTPSessionManager] PUT:path parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *) task.response;
if (urlResponse.statusCode == 500) {
NSDictionary *successDict = responseObject;
NSString *errorStr = successDict[@"error"];
if (errorStr) {
NSError *error = [NSError errorWithDomain:@"httpError" code:500 userInfo:@{NSLocalizedDescriptionKey: errorStr}];
//回调错误
}
else {
NSError *error = [NSError errorWithDomain:@"httpError" code:500 userInfo:@{NSLocalizedDescriptionKey:@"网络请求错误(500)"}];
//回调错误
}
}
else {
//回调成功的数据
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if (error) {
//回调错误
}
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
4.DELETE方法
[[HttpTool AFHTTPSessionManager] DELETE:path parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *) task.response;
if (urlResponse.statusCode == 500) {
NSDictionary *successDict = responseObject;
NSString *errorStr = successDict[@"error"];
if (errorStr) {
NSError *error = [NSError errorWithDomain:@"httpError" code:500 userInfo:@{NSLocalizedDescriptionKey: errorStr}];
//回调错误信息
}
else {
NSError *error = [NSError errorWithDomain:@"httpError" code:500 userInfo:@{NSLocalizedDescriptionKey:@"网络请求错误(500)"}];
//回调错误信息
}
}
else {
//回调成功数据
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if (error) {
//回调错误信息
}
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
5.上传文件,这里的identifier是用来做每一个附件的标识符,根据每一个不同的标识符返回不同的进度,这里涉及到附件批量上传以及进度回调就不在这里进行详细的说明了
NSString *fileName = params[@"fileName"];
if (!fileName || [fileName isEqualToString:@""]) {
fileName = [[PhotoNameModel new] photoTimeName];
}
[[HTTP AFHTTPSessionManager] POST:path parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
[formData appendPartWithFileData:data name:@"attachments" fileName:fileName mimeType:@"image/jpeg"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
NSString *identifier = [params stringForKey:@"identifier"];
NSDictionary *progressDic = @{@"totalBytesSent"
:@(uploadProgress.completedUnitCount),@"totalBytesExpectedToSend"
:@(uploadProgress.totalUnitCount),@"identifier":identifier};
if (identifier && ![identifier isEqualToString:@""]) {
success(progressDic);//回调进度
}
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (success) {
success(responseObject);//进度完成回调成功数据
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (error) {
if (fail) {
fail(error);//回调错误信息
}
}
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
6.下载文件
static NSString *tempDirName = @"Downloads";
NSURL *documentsDirectoryURL = [fileManager URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
NSString *tempPath = [documentsDirectoryURL.absoluteString stringByReplacingOccurrencesOfString:@"file://" withString:@""];
NSString * fileDir = [NSString stringWithFormat:@"%@/%@", tempPath,tempDirName];
NSURL *filePath = [NSURL fileURLWithPath:fileDir]
NSURL *fileUrl = [NSURL URLWithString:urlStr];//地址
//创建管理者对象
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:fileUrl];
NSURLSessionDownloadTask *downTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
double precent = (double)downloadProgress.completedUnitCount / (double)downloadProgress.totalUnitCount;
NSLog(@"%lf",precent);
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSHTTPURLResponse *newResponse = (NSHTTPURLResponse *)response;
if (newResponse.statusCode == 200) {
//设置下载路径,通过沙盒获取缓存地址,最后返回NSURL对象
return filePath;
}
return nil;
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
if (error) {
NSLog(@"%@",[error localizedDescription]);
} else {
if ([fileManager fileExistsAtPath:filePath]) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//这里做成功的处理
});
}
}
}
];
[downTask resume];
}

好了关于AFNetWorking的使用就基本介绍完了,从上面的代码中我们可以看到,官方的API虽然很不错,但是相对于AFNetWorking的清晰结构化来说,AF比官方的API调用起来就更加的灵活方便,使用起来也是得心应手。相对来说,我们将AF再做个二次封装作为一个公共方法,传入我们地址和参数,在外部调用的时候就更加的方便了。这种灵活的封装让我们的效率提升了不少。

下面再简单的介绍一个关于二次封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
1.封装AFNetWorking
#import "HttpTool.h"
#import "AFNetworking.h"
typedef NS_ENUM(NSUInteger, AFRequestType) {
AFRequestTypeGET,
AFRequestTypePOST,
AFRequestTypePUT,
AFRequestTypeDELETE,
};
@interface HttpTool : NSObject
//定义属性方法进行回调
+ (void)requestWithType:(AFRequestType)type path:(NSString *)path params:(NSDictionary *)param success:(HttpSuccessBlock)success fail:(HttpFailBlock)fail;
@end
@implementation HttpTool
+ (void)requestWithType:(AFRequestType)type path:(NSString *)path params:(id)param success:(HttpSuccessBlock)success fail:(HttpFailBlock)fail
{
if (type == AFRequestTypeGET) {
//GET请求
} else if (type == AFRequestTypePOST) {
//POST请求
} else if (type == AFRequestTypePUT) {
//PUT请求
} else if (type == AFRequestTypeDELETE) {
//DELETE请求
}
}
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2.封装HTTPTool
#import "HTTPRequest.h"
#import "HttpTool.h"
@interface HTTPRequest : NSObject
//请求的URl地址
@property (copy,nonatomic) NSString *requestURL;
//原始URL
@property (nonatomic, strong) NSString *requestBaseURL;
// 请求类型
@property (assign,nonatomic) AFRequestType requestType;
//定义类方法进行值传递
+ (HTTPRequest *)requestWithURL:(NSString *)url baseURL:(NSString *)baseURL reuqestType:(AFRequestType)requestType;
//定义对象方法进行回调处理
- (void)request:(id)params successBlock:(HttpSuccessBlock)successBlock failBlock:(HttpFailBlock)failBlock;
@end
@implementation HTTPRequest
+ (HTTPRequest *)requestWithURL:(NSString *)url baseURL:(NSString *)baseURL reuqestType:(AFRequestType)requestType {
HTTPRequest *request = [[HTTPRequest alloc] init];
request.requestURL = url;
request.requestType = requestType;
request.requestBaseURL = baseURL;
return request;
}
- (void)request:(id)params successBlock:(HttpSuccessBlock)successBlock failBlock:(HttpFailBlock)failBlock {
NSString *url = [NSString stringWithFormat:@"%@%@",self.requestBaseURL,self.requestURL]
[HttpTool requestWithType:self.requestType path:url params:params success:successBlock fail:failBlock];
}
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
3.继承HTTPRequest
#import "HTTPRequest.h"
#import "TasksRequest.h"
@interface TasksRequest : HTTPRequest
//获取任务列表数据
+ (HTTPRequest *)getTaskLists;
@end
@implementation TasksRequest
+ (HTTPRequest *)getTaskLists {
return [HTTPRequest requestWithURL:@"/task/query" baseURL:kTaskURL reuqestType:AFRequestTypeGET];
}
@end
1
2
3
4
5
6
7
8
9
10
11
4.外部调用
注:block的回调需要注意循环引用的问题,如果你的项目引入了RAC那么可以直接引用如下方法
@weakify(self)
[[TasksRequest getTaskLists] request:params successBlock:^(id success) {
@strongify(self)
//处理成功数据
} failBlock:^(NSError *error) {
//处理失败信息
}];

OK,介绍到这里文章差不多到了最后,二次封装带给我们的是工作效率显著的提升,这样我们不用每次请求都去调用AF显示很繁琐,同时也做了很多无用功,经过二次封装之后,我们的项目的网络请求就在AF本身的轻量级的基础上显得更加的方便,希望这篇文章能帮助到你。