计划把公司的网络请求与业务解耦,所以想着学习一下网络请求,最近学习了 NSURLSession,今天来学习一下基于 NSURLSession 封装的优秀开源框架 AFNetWorking 3.x,之前 13 年做 iOS 开发时用的 ASIHttpRequest 开源框架。
AFNetWorking 一款轻量级网络请求开源框架,基于 iOS 和 mac os 网络进行扩展的高性能框架,大大降低了 iOS 开发工程师处理网络请求的难度,让 iOS 开发变成一件愉快的事情。
GitHub 地址:https://github.com/AFNetworking/AFNetworking
- -(AFHTTPSessionManager *)sharedManager
- {
- AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
- //最大请求并发任务数
- manager.operationQueue.maxConcurrentOperationCount = 5;
- // 请求格式
- // AFHTTPRequestSerializer 二进制格式
- // AFJSONRequestSerializer JSON
- // AFPropertyListRequestSerializer PList(是一种特殊的XML,解析起来相对容易)
- manager.requestSerializer = [AFHTTPRequestSerializer serializer]; // 上传普通格式
- // 超时时间
- manager.requestSerializer.timeoutInterval = 30.0f;
- // 设置请求头
- [manager.requestSerializer setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
- // 设置接收的Content-Type
- manager.responseSerializer.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml",@"text/html", @"application/json",@"text/plain",nil];
- // 返回格式
- // AFHTTPResponseSerializer 二进制格式
- // AFJSONResponseSerializer JSON
- // AFXMLParserResponseSerializer XML,只能返回XMLParser,还需要自己通过代理方法解析
- // AFXMLDocumentResponseSerializer (Mac OS X)
- // AFPropertyListResponseSerializer PList
- // AFImageResponseSerializer Image
- // AFCompoundResponseSerializer 组合
- manager.responseSerializer = [AFJSONResponseSerializer serializer];//返回格式 JSON
- //设置返回C的ontent-type
- manager.responseSerializer.acceptableContentTypes=[[NSSet alloc] initWithObjects:@"application/xml", @"text/xml",@"text/html", @"application/json",@"text/plain",nil];
- return manager;
- }
- - (void) doGetRequest {
- //创建请求地址
- NSString * url = @"http://api.nohttp.net/method";
- //构造参数
- NSDictionary * parameters = @ {@"name": @"yanzhenjie",
- @"pwd": @"123"
- };
- //AFN管理者调用get请求方法
- [[self shareAFNManager] GET: url parameters: parameters progress: ^(NSProgress * _Nonnull downloadProgress) {
- //返回请求返回进度
- NSLog(@"downloadProgress-->%@", downloadProgress);
- }
- success: ^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- //请求成功返回数据 根据responseSerializer 返回不同的数据格式
- NSLog(@"responseObject-->%@", responseObject);
- }
- failure: ^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- //请求失败
- NSLog(@"error-->%@", error);
- }];
- }
- - (void) doPostRequestOfAFN {
- //创建请求地址
- NSString * url = @"http://api.nohttp.net/postBody";
- //构造参数
- NSDictionary * parameters = @ {@"name": @"yanzhenjie",
- @"pwd": @"123"
- };
- //AFN管理者调用get请求方法
- [[self shareAFNManager] POST: url parameters: parameters progress: ^(NSProgress * _Nonnull uploadProgress) {
- //返回请求返回进度
- NSLog(@"downloadProgress-->%@", uploadProgress);
- }
- success: ^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- //请求成功返回数据 根据responseSerializer 返回不同的数据格式
- NSLog(@"responseObject-->%@", responseObject);
- }
- failure: ^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- //请求失败
- NSLog(@"error-->%@", error);
- }];
- }
- - (void) doUploadRequest {
- // 创建URL资源地址
- NSString * url = @"http://api.nohttp.net/upload";
- // 参数
- NSDictionary * parameters = @ {@"name": @"yanzhenjie",
- @"pwd": @"123"
- }; [[self shareAFNManager] POST: url parameters: parameters constructingBodyWithBlock: ^(id _Nonnull formData) {
- NSDate * dat = [NSDate dateWithTimeIntervalSinceNow: 0];
- NSTimeInterval a = [dat timeIntervalSince1970];
- NSString * fileName = [NSString stringWithFormat: @"file_%0.f.txt", a];
- [FileUtils writeDataToFile: fileName data: [@"upload_file_to_server"dataUsingEncoding: NSUTF8StringEncoding]];
- // 获取数据转换成data
- NSString * filePath = [FileUtils getFilePath: fileName];
- // 拼接数据到请求题中
- [formData appendPartWithFileURL: [NSURL fileURLWithPath: filePath] name: @"headUrl"fileName: fileName mimeType: @"application/octet-stream"error: nil];
- }
- progress: ^(NSProgress * _Nonnull uploadProgress) {
- // 上传进度
- NSLog(@"%lf", 1.0 * uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
- }
- success: ^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- //请求成功
- NSLog(@"请求成功:%@", responseObject);
- }
- failure: ^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- //请求失败
- NSLog(@"请求失败:%@", error);
- }];
- }
- -(void)doDownLoadRequest
- {
- NSString *urlStr =@"http://images2015.cnblogs.com/blog/950883/201701/950883-20170105104233581-62069155.png";
- // 设置请求的URL地址
- NSURL *url = [NSURL URLWithString:urlStr];
- // 创建请求对象
- NSURLRequest *request = [NSURLRequest requestWithURL:url];
- // 下载任务
- NSURLSessionDownloadTask *task = [[self shareAFNManager] downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
- // 下载进度
- NSLog(@"当前下载进度为:%lf", 1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount);
- } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
- // 下载地址
- NSLog(@"默认下载地址%@",targetPath);
- //这里模拟一个路径 真实场景可以根据url计算出一个md5值 作为fileKey
- NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0];
- NSTimeInterval a=[dat timeIntervalSince1970];
- NSString* fileKey = [NSString stringWithFormat:@"/file_%0.f.txt", a];
- // 设置下载路径,通过沙盒获取缓存地址,最后返回NSURL对象
- NSString *filePath = [FileUtils getFilePath:fileKey];
- return [NSURL fileURLWithPath:filePath]; // 返回的是文件存放在本地沙盒的地址
- } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
- // 下载完成调用的方法
- NSLog(@"filePath---%@", filePath);
- NSData *data=[NSData dataWithContentsOfURL:filePath];
- UIImage *image=[UIImage imageWithData:data];
- // 刷新界面...
- UIImageView *imageView =[[UIImageView alloc]init];
- imageView.image=image;
- [self.view addSubview:imageView];
- [imageView mas_makeConstraints:^(MASConstraintMaker *make) {
- make.center.equalTo(self.view);
- make.size.mas_equalTo(CGSizeMake(300, 300));
- }];
- }];
- //启动下载任务
- [task resume];
- }
- - (void)aFNetworkStatus{
- //创建网络监测者
- AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
- /*枚举里面四个状态 分别对应 未知 无网络 数据 WiFi
- typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
- AFNetworkReachabilityStatusUnknown = -1, 未知
- AFNetworkReachabilityStatusNotReachable = 0, 无网络
- AFNetworkReachabilityStatusReachableViaWWAN = 1, 蜂窝数据网络
- AFNetworkReachabilityStatusReachableViaWiFi = 2, WiFi
- };
- */
- [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
- //这里是监测到网络改变的block 可以写成switch方便
- //在里面可以随便写事件
- switch (status) {
- case AFNetworkReachabilityStatusUnknown:
- NSLog(@"未知网络状态");
- break;
- case AFNetworkReachabilityStatusNotReachable:
- NSLog(@"无网络");
- break;
- case AFNetworkReachabilityStatusReachableViaWWAN:
- NSLog(@"蜂窝数据网");
- break;
- case AFNetworkReachabilityStatusReachableViaWiFi:
- NSLog(@"WiFi网络");
- break;
- default:
- break;
- }
- }] ;
- [manager startMonitoring];
- }
通常情况我们一般会认为以 manager 结尾的都是单例模式,所以我们一般都是这样使用 AFNetWorking,如下
- AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
其实我们点进去查看源码发现并不是单例,而是每次都实例化一个 AFHTTPSessionManager 对象,源码如下
- + (instancetype)manager {
- return [[[self class] alloc] initWithBaseURL:nil];
- }
所以我们在使用 AFNetWorking 的时候要对 AFHTTPSessionManager 进行单例封装
- + (AFHTTPSessionManager *)sharedManager
- {
- static AFHTTPSessionManager *manager = nil;
- static dispatch_once_t predicate;
- dispatch_once(&predicate, ^{
- manager = [AFHTTPSessionManager manager];
- manager.operationQueue.maxConcurrentOperationCount = 5;
- manager.requestSerializer.timeoutInterval=30.f;
- manager.responseSerializer.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml",@"text/html", @"application/json",@"text/plain",nil];
- [manager.requestSerializer setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
- });
- return manager;
- }
在 2017 年 1 月 1 日起 Apple 要求开发者于年底之前为提交至 App Store 中的应用启用 HTTPS ,以支持 iOS 9 引入的 ATS(App Transport Security)技术。但后来,apple 发布声明宣布延长这个时限,提供给开发者更多的时间进行相关准备。目前 Apple 尚未公布新的截止日期。所以目前应对 https 的方案有两种。
第一种方式:
屏蔽调 iOS ATS(App Transport Security),在 pList.info 文件中添加如下代码
- NSAppTransportSecurity
- NSAllowsArbitraryLoads
- <true/>
第二种方式:
配置 https CA 证书,这里采用获取 NSBundle 中获取 CA 证书,AFNetWorking 提供了配置 AFSecurityPolicy 模块
- + (AFSecurityPolicy *)customSecurityPolicy{
- //Https CA证书地址
- NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"XueLeTSHTTPS" ofType:@"cer"];
- //获取CA证书数据
- NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
- //创建AFSecurityPolicy对象
- AFSecurityPolicy *security = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
- //设置是否允许不信任的证书(证书无效、证书时间过期)通过验证 ,默认为NO.
- security.allowInvalidCertificates = YES;
- //是否验证域名证书的CN(common name)字段。默认值为YES。
- security.validatesDomainName = NO;
- //根据验证模式来返回用于验证服务器的证书
- security.pinnedCertificates = [NSSet setWithObject:cerData];
- return security;
- }
然后通过设置 AFHTTPSessionManager 的 securityPolicy 属性等于自定义的 AFSecurityPolicy。
简单记录一下 AFNetWorking 的基本使用,方便以后查找。
来源: