DKNetworking 基于 AFNetworking + YYCache 的网络请求框架

前言

网络层是 APP 架构的一个重要部分,苹果的 CFNetwork 框架极其难用,导致基于 CFN 的框架 ASI 已经放弃维护。后来苹果推出了 NSURLSession,许多开源的框架都是基于它进行封装,例如我选用的 AFNetworking,运行效率没有 ASI 高,使用比 ASI 简单。但是,从使用的角度来看,还需要继续封装。于是,我依赖于 AFN + YYCache + MJExtension 封装出了 DKNetworking 框架。

特点

  • DKNetworkSessionManager 对 AFHTTPSessionManager 进行封装,以便以后可以轻易换掉依赖的 AFN。封装一层底层方法,包括网络请求、文件上传、文件下载这三个方法。其中网络请求调用了 AFN 的私有 API,把 GET、POST、PUT、DELETE、PATCH 这五个方法封成了一个 request 方法。

  • 拥有 AFN 大部分常用功能,包括网络状态监听等,提供类方法和实例方法调用。

  • 根据 JavaWeb 中 HttpServlet 的设计思想,把网络请求的回调封装成了 request 和 response 两个对象。

  • 支持界面多级缓存。

  • 支持链式调用。

  • 支持配合 RAC 框架使用链式调用,支持自定义信号的返回值。

安装

支持 Cocoapods 安装

pod 'DKNetworking'

或者下载 Demo 项目

  • 将根目录下的 DKNetworking 和 Vendor 文件夹拉进项目,其中 Vendor 文件夹有 AFNetworking、MJExtension、YYCache 和 ReactiveCocoa,前三个是必须依赖的,ReactiveCocoa 是可选的。

  • 必须在 Target -> Build Phases -> Link Binary With Libraries 中添加 libsqlite3.0.tbd,这是 YYCache 框架所需的。

使用

绝大部分方法都可以直接看 DKNetworking.h 中的声明以及注释。

获取单例对象

DKNetworking *networking = [DKNetworking networkManager];

从 1.1.0 版本开始提供了一个单例对象宏 DKNetworkManager

DKN 配置

设置请求根路径

[DKNetworking setupBaseURL:@"https://m.sfddj.com/app/v1/"];

baseURL 的路径一定要有“/”结尾,设置后所有的网络访问都使用相对路径。

设置日志

开启日志打印
[DKNetworking openLog];
关闭日志打印
[DKNetworking closeLog];

设置缓存

设置缓存方式
/**
 设置缓存方式
 DKNetworkCacheTypeNetworkOnly : 只加载网络数据
 DKNetworkCacheTypeCacheNetwork : 先加载缓存,然后加载网络
 @param cacheType 缓存类型
 */
+ (void)setupCacheType:(DKNetworkCacheType)cacheType;

获取缓存大小
/**
 获取网络缓存的总大小 动态单位(GB,MB,KB,B)

 @return 网络缓存的总大小
 */
+ (NSString *)cacheSize;
清除缓存
[DKNetworkCache clearCache];

设置序列化格式

设置请求序列化格式
/**
 设置网络请求参数的格式 : 默认为二进制格式

 @param requestSerializer DKRequestSerializerJSON:JSON格式, DKRequestSerializerHTTP:二进制格式
 */
+ (void)setRequestSerializer:(DKRequestSerializer)requestSerializer;
设置响应反序列化格式
/**
 设置服务器响应数据格式 : 默认为JSON格式

 @param responseSerializer DKResponseSerializerJSON:JSON格式, DKResponseSerializerHTTP:二进制格式
 */
+ (void)setResponseSerializer:(DKResponseSerializer)responseSerializer;

设置请求超时时间

/**
 设置请求超时时间 : 默认10秒

 @param time 请求超时时长(秒)
 */
+ (void)setRequestTimeoutInterval:(NSTimeInterval)time;

设置 Header

设置一对请求头参数
/**
 设置一对请求头参数

 @param value 请求头参数值
 @param field 请求头参数名
 */
+ (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
设置多对请求头参数
/**
 设置多对请求头参数

 @param networkHeader 请求头参数字典
 */
+ (void)setNetworkHeader:(NSDictionary *)networkHeader;

网络状态

监听网络状态

[DKNetworking networkStatusWithBlock:^(DKNetworkStatus status) {
    switch (status) {
        case DKNetworkStatusUnknown: // 网络状态未知
            break;
        case DKNetworkStatusNotReachable: // 无网络
            break;
        case DKNetworkStatusReachableViaWWAN: // 手机网络(蜂窝)
            break;
        case DKNetworkStatusReachableViaWiFi: // WIFI网络
            break;
    }
}];

获取当前网络状态

/**
 有网络:YES, 无网络:NO
 */
+ (BOOL)isNetworking;

/**
 手机网络:YES, 非手机网络:NO
 */
+ (BOOL)isWWANNetwork;

/**
 WiFi网络:YES, 非WiFi网络:NO
 */
+ (BOOL)isWiFiNetwork;

网络请求

常规调用

以 POST 方法为例,方法定义:

/**
 POST请求
 
 @param URL 请求地址
 @param parameters 请求参数
 @param callback 请求回调
 @return 返回的对象可取消请求,调用cancel方法
 */
+ (NSURLSessionTask *)POST:(NSString *)URL parameters:(NSDictionary *)parameters callback:(DKNetworkBlock)callback;
- (NSURLSessionTask *)POST:(NSString *)URL parameters:(NSDictionary *)parameters callback:(DKNetworkBlock)callback;

调用:

[DKNetworking POST:url parameters:@{@"name":@"bingo"} callback:^(DKNetworkRequest *request, DKNetworkResponse *response) {
    // ...
}];

链式调用

方法定义:

/** 链式调用 */
- (DKNetworking *(^)(NSString *url))get;
- (DKNetworking *(^)(NSString *url))post;
- (DKNetworking *(^)(NSString *url))put;
- (DKNetworking *(^)(NSString *url))delete;
- (DKNetworking *(^)(NSString *url))patch;
- (DKNetworking *(^)(NSDictionary *params))params;
- (DKNetworking *(^)(NSDictionary *header))header;
- (DKNetworking *(^)(DKNetworkCacheType cacheType))cacheType;
- (DKNetworking *(^)(DKRequestSerializer requestSerializer))requestSerializer;
- (DKNetworking *(^)(DKResponseSerializer responseSerializer))responseSerializer;
- (DKNetworking *(^)(DKRequestTimeoutInterval requestTimeoutInterval))requestTimeoutInterval;
- (void(^)(DKNetworkBlock networkBlock))callback;

调用:

DKNetworkManager.post(url).params(@{@"name":@"bingo"}).callback(^(DKNetworkRequest *request, DKNetworkResponse *response) {
    // ...
});

RAC 链式调用

方法定义:

在原链式方法的基础上补充了一个 executeSignal 方法,返回信号。

#ifdef RAC
/** RAC链式发送请求 */
- (RACSignal *)executeSignal;
#endif

调用:

[DKNetworkManager.post(url).params(@{@"name":@"bingo"}).executeSignal subscribeNext:^(RACTuple *x) {
    DKNetworkResponse *response = x.second;
    // ...
} error:^(NSError *error) {
    // ...
}];

RAC 链式调用 自定义信号的返回值

方法定义:

#ifdef RAC
/**
 设置响应结果回调,可以设置信号返回的value为自己想要的值,比如用MJExtension框架,将DKNetworkResponse对象的rawData字典转为自己项目的实体类再返回
 
 @param flattenMapBlock 结果映射的设置回调block,其中RACTuple的first为DKNetworkRequest对象,second为DKNetworkResponse对象
 */
+ (void)setupResponseSignalWithFlattenMapBlock:(DKNetworkFlattenMapBlock)flattenMapBlock;
#endif

使用:

考虑到代码执行顺序的问题,建议在项目中创建一个继承自 NSObject 的类 DKNetworkConfig,在 + load 方法中调用 DKN 的配置方法,设置回调的信号的 return 值,根据不同项目进行配置。

[DKNetworking setupResponseSignalWithFlattenMapBlock:^RACStream *(RACTuple *tuple) {
    DKNetworkResponse *response = tuple.second; // 框架默认返回的response
    MyHttpResponse *myResponse = [MyHttpResponse mj_objectWithKeyValues:response.rawData]; // 项目需要的response
    myResponse.rawData = response.rawData;
    myResponse.error = response.error;
    return [RACSignal return:RACTuplePack(tuple.first, myResponse)];
}];

调用:

经过上面的setupResponseSignalWithFlattenMapBlock:方法设置后,信号返回的 RACTuple 的 second 为自定义的 response 对象。

[DKNetworkManager.post(url).executeSignal subscribeNext:^(RACTuple *x) {
//        DKNetworkResponse *response = x.second;
    MyHttpResponse *myResponse = x.second;
    // ...
} error:^(NSError *error) {
    // ...
}];

取消请求

/**
 取消所有HTTP请求
 */
+ (void)cancelAllRequest;

/**
 取消指定URL的HTTP请求
 */
+ (void)cancelRequestWithURL:(NSString *)URL;

上传

上传文件
/**
 上传文件

 @param URL 请求地址
 @param parameters 请求参数
 @param name 文件对应服务器上的字段
 @param filePath 文件本地的沙盒路径
 @param progressBlock 上传进度回调
 @param callback 请求回调
 @return 返回的对象可取消请求,调用cancel方法
 */
+ (NSURLSessionTask *)uploadFileWithURL:(NSString *)URL
                             parameters:(NSDictionary *)parameters
                                   name:(NSString *)name
                               filePath:(NSString *)filePath
                          progressBlock:(DKNetworkProgressBlock)progressBlock
                               callback:(void(^)(DKNetworkResponse *response))callback;
上传图片
/**
 上传图片

 @param URL 请求地址
 @param parameters 请求参数
 @param name 图片对应服务器上的字段
 @param images 图片数组
 @param fileNames 图片文件名数组,传入nil时数组内的文件名默认为当前日期时间戳+索引
 @param imageScale 图片文件压缩比 范围 (0.f ~ 1.f)
 @param imageType 图片文件的类型,例:png、jpg(默认类型)....
 @param progressBlock 上传进度回调
 @param callback 请求回调
 @return 返回的对象可取消请求,调用cancel方法
 */
+ (NSURLSessionTask *)uploadImagesWithURL:(NSString *)URL
                               parameters:(NSDictionary *)parameters
                                     name:(NSString *)name
                                   images:(NSArray *)images
                                fileNames:(NSArray *)fileNames
                               imageScale:(CGFloat)imageScale
                                imageType:(NSString *)imageType
                            progressBlock:(DKNetworkProgressBlock)progressBlock
                                 callback:(void(^)(DKNetworkResponse *response))callback;

下载

/**
 下载文件

 @param URL 请求地址
 @param fileDir 文件存储目录(默认存储目录为Download)
 @param progressBlock 文件下载的进度回调
 @param callback 请求回调,filePath为文件保存路径
 @return 返回 NSURLSessionDownloadTask 实例,可用于暂停继续,暂停调用 suspend 方法,开始下载调用 resume 方法
 */
+ (NSURLSessionTask *)downloadWithURL:(NSString *)URL
                              fileDir:(NSString *)fileDir
                        progressBlock:(DKNetworkProgressBlock)progressBlock
                             callback:(void(^)(NSString *filePath, NSError *error))callback;

TODO

  • 链式编程虽然好看一点,但是没有常规调用方便,不能像以往一样敲两个字母 Xcode 就智能补全整个方法,并且可能会出现 .get().post().get().post() 这种没有意义的链式调用的情况。

  • 用面向协议(接口)的函数式编程可以解决上面的问题,但是网络层工具的调用是可以没有顺序的,比如 .params().post().post().params() 应该是都可以的,这样的话会让协议很难制定,还是会出现上面的问题。

  • 日志保存文件

  • 考虑添加 UI 层分类,让网络请求的错误日志以一种更友好的方式展示。

  • 与服务端的通讯加密,考虑添加 RSA 非对称加密等方法。

后话

DKNetworking 框架源码已经托管在 GitHub,遵循 MIT 协议。欢迎向我们发起 PR 贡献您的代码,或者有什么建议欢迎 Issues。如果对您有帮助,请给予 star,你们的支持是我奋斗的动力,谢谢!

你可能感兴趣的:(DKNetworking 基于 AFNetworking + YYCache 的网络请求框架)