现在项目处于维护阶段,以前的一部分代码随着用户量的增加,不断暴露出问题,特别是网络请求--回调处理方面的修改比较多。每次修改对应的接口都要跳到对应控制器啊,或者是搜一发提示语,其他网络请求操作也是分散在各个控制器内,乱七八糟。好烦的。趁着有点空闲,研究了下别人的封装,改进了项目中使用的封装,并做成了SDK发布在cocoaPods上,欢迎使用.
pod 'TYNetworkTool'
即可使用。
项目原先的AFN封装
也就是很简单的按照AFN封装了一下,.m
//
// PPRHttpManager.m
// PaopaoRunning
//
// Created by 王天永 on 16/5/22.
//
#import "PPRHttpManager.h"
#import
#import "NSString+Paths.h"
#import "NSString+UUID.h"
@implementation PPRHttpManager
+ (NSURLSessionDataTask *)get:(NSString *)url params:(NSDictionary *)params success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
if (IOS11) { //适配IOS11增加的这句
manager.securityPolicy.allowInvalidCertificates = YES;
}
manager.requestSerializer.timeoutInterval = 15;
// [manager.requestSerializer setValue:@"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
NSURLSessionDataTask *task = [manager GET:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (success){
NSString *responseString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
PRLog(@"responseString---%@",responseString);
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
success(dict);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (failure){
failure(error);
}
}];
return task;
}
+ (NSURLSessionDataTask *)post:(NSString *)url params:(NSDictionary *)params success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
// manager.securityPolicy.allowInvalidCertificates = YES;
manager.requestSerializer.timeoutInterval = 15;
NSURLSessionDataTask *task = [manager POST:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (success){
NSString *responseString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
PRLog(@"responseString---%@",responseString);
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
success(dict);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (failure){
failure(error);
}
}];
return task;
}
+ (NSURLSessionDownloadTask *)downloadFile:(NSString *)url progress:(void (^)(CGFloat progress))progress success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
// NSLog(@"progress is %f", downloadProgress.fractionCompleted);
progress(downloadProgress.fractionCompleted);
} destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSString *caches = [NSString cachesPath];
NSString *videovideoCaches = [caches stringByAppendingPathComponent:@"videoCaches"];
NSURL *videoFileUrl = [NSURL fileURLWithPath:videovideoCaches];
return [videoFileUrl URLByAppendingPathComponent:[response suggestedFilename] ];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
if (!error){
PRLog(@"%@",F(@"File downloaded to: %@", filePath));
success(filePath);
}else{
PRLog(@"File download fail!");
failure(error);
}
}];
[downloadTask resume];
return downloadTask;
}
@end
着手封装新的低耦合的AFN网络封装
封装的主要方向
- 本地缓存优化,请求可根据需求(缓存的时效性等)决定是否直接使用本地缓存,或者请求到新数据后刷新本地。(当前已完成)
- 增加短时缓存,避免相同的请求重复调用,浪费用户流量。(当前已完成)
- 封装请求失败的重试机制。
- 将成功失败提示的逻辑封装。
- 其他比如网络状况监控,请求一键撤销等。(当前已完成)
感谢巨巨文章提供思路和参考
猿题库YTKNetwork的GitHub
iOS 工作中封装通用性网络请求框架——鸿雁长飞光不度
AFNetworking3.x与YYCache的二次封装,和FMDB说拜拜——jkpang
封装的结构考虑
- 考虑以后换网络框架的可能。(网络单独封装
- 考虑以后换回调处理及提示的可能。网络请求和回调处理要低耦合,能够单独抽离。(回调和显示的单独封装
- 考虑以后换缓存框架的可能。(缓存单独封装
封装的结构
考虑到网络请求的通用性,而回调处理和提示等并不通用,所以回调的封装暂时不纳入SDK,但是仍然建议将网络请求的回调封装起来存于一处,相同回调调用相应的统一方法,以后需要改动改这一处就够了,如本人GitHub上的TYNetworkManage
文件夹内所封装的方式。
所以最终的SDK内的文件结构是这样的(暂时
· 第一层 网络请求入口
TYNetworkTool
,.h文件对外暴露,用户之间调用即可完成网络请求。.m调用
TYCacheTool.h
的方法来完成缓存的存储和读取;
· 第二层 缓存的存取入口
TYCacheTool
,主要来完成缓存的业务逻辑
· 第三层 FMDB的实际缓存操作,实际操控SQLite文件来进行存取操作。
文件内容
//
// TYNetworkTool.h
// TYNetworkHelper
//
// Created by 王天永 on 2017/7/14.
// Copyright © 2017年 王天永. All rights reserved.
//
#import
#import
#import "TYCacheTool.h"
typedef NS_ENUM(NSUInteger, TYNetworkStatusType) {
/// 未知网络
TYNetworkStatusUnknown,
/// 无网络
TYNetworkStatusNotReachable,
/// 手机网络
TYNetworkStatusReachableViaWWAN,
/// WIFI网络
TYNetworkStatusReachableViaWiFi
};
typedef NS_ENUM(NSUInteger, TYRequestSerializer) {
/// 设置请求数据为JSON格式
TYRequestSerializerJSON,
/// 设置请求数据为二进制格式
TYRequestSerializerHTTP,
};
typedef NS_ENUM(NSUInteger, TYResponseSerializer) {
/// 设置响应数据为JSON格式
TYResponseSerializerJSON,
/// 设置响应数据为二进制格式
TYResponseSerializerHTTP,
};
/// 请求成功的Block
typedef void(^TYHttpRequestSuccess)(id responseObject);
/// 请求失败的Block
typedef void(^TYHttpRequestFailed)(NSError *error);
/// 缓存的Block
typedef void(^TYHttpRequestCache)(id responseCache);
/// 上传或者下载的进度, Progress.completedUnitCount:当前大小 - Progress.totalUnitCount:总大小
typedef void (^TYHttTYrogress)(NSProgress *progress);
/// 网络状态的Block
typedef void(^TYNetworkStatus)(TYNetworkStatusType status);
@class AFHTTPSessionManager;
@interface TYNetworkTool : NSObject
/// 有网YES, 无网:NO
+ (BOOL)isNetwork;
/// 手机网络:YES, 反之:NO
+ (BOOL)isWWANNetwork;
/// WiFi网络:YES, 反之:NO
+ (BOOL)isWiFiNetwork;
/// 取消所有HTTP请求
+ (void)cancelAllRequest;
/// 实时获取网络状态,通过Block回调实时获取(此方法可多次调用)
+ (void)networkStatusWithBlock:(TYNetworkStatus)networkStatus;
/// 取消指定URL的HTTP请求
+ (void)cancelRequestWithURL:(NSString *)URL;
/// 开启日志打印 (Debug级别)
+ (void)openLog;
/// 关闭日志打印,默认关闭
+ (void)closeLog;
/**
* GET请求,无缓存
*
* @param URL 请求地址
* @param parameters 请求参数
* @param success 请求成功的回调
* @param failure 请求失败的回调
*
* @return 返回的对象可取消请求,调用cancel方法
*/
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
parameters:(id)parameters
success:(TYHttpRequestSuccess)success
failure:(TYHttpRequestFailed)failure;
/**
* GET请求,自动缓存
*
* @param URL 请求地址
* @param parameters 请求参数
* @param responseCache 缓存数据的回调
* @param success 请求成功的回调
* @param failure 请求失败的回调
*
* @return 返回的对象可取消请求,调用cancel方法
*/
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
parameters:(id)parameters
responseCache:(TYHttpRequestCache)responseCache
success:(TYHttpRequestSuccess)success
failure:(TYHttpRequestFailed)failure;
/**
GET请求,带时效自动缓存
@param URL 请求地址
@param parameters 请求参数
@param life 缓存时效
@param responseCache 缓存数据的回调
@param success 请求成功的回调
@param failure 请求失败的回调
@return 返回的对象可取消请求,调用cancel方法
*/
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
parameters:(id)parameters
userfulLifeUnit:(TYTimeUnit)timeUnit
userfullLife:(double)life
responseCache:(TYHttpRequestCache)responseCache
success:(TYHttpRequestSuccess)success
failure:(TYHttpRequestFailed)failure;
/**
* POST请求,无缓存
*
* @param URL 请求地址``
* @param parameters 请求参数
* @param success 请求成功的回调
* @param failure 请求失败的回调
*
* @return 返回的对象可取消请求,调用cancel方法
*/
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
parameters:(id)parameters
success:(TYHttpRequestSuccess)success
failure:(TYHttpRequestFailed)failure;
/**
* POST请求,自动缓存
*
* @param URL 请求地址
* @param parameters 请求参数
* @param responseCache 缓存数据的回调
* @param success 请求成功的回调
* @param failure 请求失败的回调
*
* @return 返回的对象可取消请求,调用cancel方法
*/
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
parameters:(id)parameters
responseCache:(TYHttpRequestCache)responseCache
success:(TYHttpRequestSuccess)success
failure:(TYHttpRequestFailed)failure;
/**
GET请求,带时效自动缓存
@param URL 请求地址
@param parameters 请求参数
@param life 缓存时效
@param responseCache 缓存数据的回调
@param success 请求成功的回调
@param failure 请求失败的回调
@return 返回的对象可取消请求,调用cancel方法
*/
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
parameters:(id)parameters
userfulLifeUnit:(TYTimeUnit)timeUnit
userfullLife:(double)life
responseCache:(TYHttpRequestCache)responseCache
success:(TYHttpRequestSuccess)success
failure:(TYHttpRequestFailed)failure;
/**
* 上传文件
*
* @param URL 请求地址
* @param parameters 请求参数
* @param name 文件对应服务器上的字段
* @param filePath 文件本地的沙盒路径
* @param progress 上传进度信息
* @param success 请求成功的回调
* @param failure 请求失败的回调
*
* @return 返回的对象可取消请求,调用cancel方法
*/
+ (__kindof NSURLSessionTask *)uploadFileWithURL:(NSString *)URL
parameters:(id)parameters
name:(NSString *)name
filePath:(NSString *)filePath
progress:(TYHttTYrogress)progress
success:(TYHttpRequestSuccess)success
failure:(TYHttpRequestFailed)failure;
/**
* 上传单/多张图片
*
* @param URL 请求地址
* @param parameters 请求参数
* @param name 图片对应服务器上的字段
* @param images 图片数组
* @param fileNames 图片文件名数组, 可以为nil, 数组内的文件名默认为当前日期时间"yyyyMMddHHmmss"
* @param imageScale 图片文件压缩比 范围 (0.f ~ 1.f)
* @param imageType 图片文件的类型,例:png、jpg(默认类型)....
* @param progress 上传进度信息
* @param success 请求成功的回调
* @param failure 请求失败的回调
*
* @return 返回的对象可取消请求,调用cancel方法
*/
+ (__kindof NSURLSessionTask *)uploadImagesWithURL:(NSString *)URL
parameters:(id)parameters
name:(NSString *)name
images:(NSArray *)images
fileNames:(NSArray *)fileNames
imageScale:(CGFloat)imageScale
imageType:(NSString *)imageType
progress:(TYHttTYrogress)progress
success:(TYHttpRequestSuccess)success
failure:(TYHttpRequestFailed)failure;
/**
* 下载文件
*
* @param URL 请求地址
* @param fileDir 文件存储目录(默认存储目录为Download)
* @param progress 文件下载的进度信息
* @param success 下载成功的回调(回调参数filePath:文件的路径)
* @param failure 下载失败的回调
*
* @return 返回NSURLSessionDownloadTask实例,可用于暂停继续,暂停调用suspend方法,开始下载调用resume方法
*/
+ (__kindof NSURLSessionTask *)downloadWithURL:(NSString *)URL
fileDir:(NSString *)fileDir
progress:(TYHttTYrogress)progress
success:(void(^)(NSString *filePath))success
failure:(TYHttpRequestFailed)failure;
#pragma mark - 设置AFHTTPSessionManager相关属性
#pragma mark 注意: 因为全局只有一个AFHTTPSessionManager实例,所以以下设置方式全局生效
/**
在开发中,如果以下的设置方式不满足项目的需求,就调用此方法获取AFHTTPSessionManager实例进行自定义设置
(注意: 调用此方法时在要导入AFNetworking.h头文件,否则可能会报找不到AFHTTPSessionManager的❌)
@param sessionManager AFHTTPSessionManager的实例
*/
+ (void)setAFHTTPSessionManagerProperty:(void(^)(AFHTTPSessionManager *sessionManager))sessionManager;
/**
* 设置网络请求参数的格式:默认为二进制格式
*
* @param requestSerializer TYRequestSerializerJSON(JSON格式),TYRequestSerializerHTTP(二进制格式),
*/
+ (void)setRequestSerializer:(TYRequestSerializer)requestSerializer;
/**
* 设置服务器响应数据格式:默认为JSON格式
*
* @param responseSerializer TYResponseSerializerJSON(JSON格式),TYResponseSerializerHTTP(二进制格式)
*/
+ (void)setResponseSerializer:(TYResponseSerializer)responseSerializer;
/**
* 设置请求超时时间:默认为30S
*
* @param time 时长
*/
+ (void)setRequestTimeoutInterval:(NSTimeInterval)time;
/// 设置请求头
+ (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
/**
* 是否打开网络状态转圈菊花:默认打开
*
* @param open YES(打开), NO(关闭)
*/
+ (void)openNetworkActivityIndicator:(BOOL)open;
/**
配置自建证书的Https请求, 参考链接: http://blog.csdn.net/syg90178aw/article/details/52839103
@param cerPath 自建Https证书的路径
@param validatesDomainName 是否需要验证域名,默认为YES. 如果证书的域名与请求的域名不一致,需设置为NO; 即服务器使用其他可信任机构颁发
的证书,也可以建立连接,这个非常危险, 建议打开.validatesDomainName=NO, 主要用于这种情况:客户端请求的是子域名, 而证书上的是另外
一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com, 那么mail.google.com是无法验证通过的.
*/
+ (void)setSecurityPolicyWithCerPath:(NSString *)cerPath validatesDomainName:(BOOL)validatesDomainName;
+ (NSString *)jsonToString:(id)data;
@end
//
// TYCacheTool.h
// TYNetworkTool
//
// Created by 王天永 on 2017/7/19.
// Copyright © 2017年 王天永. All rights reserved.
//
#import
typedef NS_ENUM (NSUInteger,TYTimeUnit) {
TYNone,
TYSecond,
TYMinute,
TYHour,
TYDay,
};
@interface TYCacheTool : NSObject
/**
无失效日期缓存
@param httpData 缓存数据
@param url 请求url
@param parameters 请求参数
*/
+ (void)setHttpCache:(id)httpData URL:(NSString *)url parameters:(NSDictionary *)parameters;
/**
带时效的缓存
@param httpData 缓存数据
@param url 请求url
@param parameters 请求参数
@param timeUnit 时间单位,TYNone则无时效
@param life 时效数值,具体单位与timeUnit有关,最小单位 秒
*/
+ (void)setHttpCache:(id)httpData URL:(NSString *)url parameters:(NSDictionary *)parameters userfulLifeUnit:(TYTimeUnit)timeUnit life:(double)life;
/**
获取缓存数据
@param url 请求url
@param parameter 请求参数
@return 缓存数据
*/
+ (id)httpCacheWithURL:(NSString *)url parameters:(NSDictionary *)parameter;
@end
//
// TYFMDBTool.h
// TYNetworkTool
//
// Created by 王天永 on 2017/7/20.
// Copyright © 2017年 王天永. All rights reserved.
//
#import
@class TYCacheTool;
@interface TYFMDBTool : NSObject
/**
创建tool实例,包含一个dmdb实例对象
@param name 对应账号的唯一标识符
@return 返回fmdbtool实例
*/
+ (instancetype)fmdbWithName:(NSString *)name;
/**
创建表单
@param tableName 表单名
@return FMDB实例
*/
- (instancetype)createTableWithName:(NSString *)tableName;
/**
储存缓存请求来的回调数据(无时效)
@param httpData 回调数据
@param cacheKey 存储的关键字
*/
- (void)setHttpCache:(id)httpData forCacheKey:(NSString *)cacheKey;
/**
带时效的缓存
@param httpData 缓存数据
@param cacheKey 存储的关键字
@param life 时效数值,具体单位与timeUnit有关
*/
- (void)setHttpCache:(id)httpData userfulLife:(double)life forCacheKey:(NSString *)cacheKey;
/**
获取本地缓存
@param cacheKey 存储的关键字
@return 缓存本地的网络请求
*/
- (id)httpCacheForCacheKey:(NSString *)cacheKey;
@end
感觉带时效的缓存还有改进的余地,后续再继续改进吧,以新增方法的方式。(当前的方式适合这样的需求:如果是有效的缓存,先读取了显示在界面上,等数据获取到了再显示获取到的数据。但是这样就没有达到节省流量的目的了,需要再SDK内增加先取缓存,无有效缓存再发送网络请求的方法。
本人的这个SDK已经发布在cocoapods上,pod 'TYNetworkTool'
即可使用。