前言
網(wǎng)絡(luò)層是 APP 架構(gòu)的一個(gè)重要部分博脑,蘋果的 CFNetwork 框架極其難用积仗,導(dǎo)致基于 CFN 的框架 ASI 已經(jīng)放棄維護(hù)。后來蘋果推出了 NSURLSession考润,許多開源的框架都是基于它進(jìn)行封裝棺禾,例如我選用的 AFNetworking,運(yùn)行效率沒有 ASI 高本慕,使用比 ASI 簡(jiǎn)單排拷。但是,從使用的角度來看锅尘,還需要繼續(xù)封裝监氢。于是,我依賴于 AFN + YYCache + MJExtension 封裝出了 DKNetworking 框架藤违。
特點(diǎn)
DKNetworkSessionManager 對(duì) AFHTTPSessionManager 進(jìn)行封裝浪腐,以便以后可以輕易換掉依賴的 AFN。封裝一層底層方法顿乒,包括網(wǎng)絡(luò)請(qǐng)求议街、文件上傳、文件下載這三個(gè)方法淆游。其中網(wǎng)絡(luò)請(qǐng)求調(diào)用了 AFN 的私有 API傍睹,把 GET隔盛、POST、PUT拾稳、DELETE吮炕、PATCH 這五個(gè)方法封成了一個(gè) request 方法。
擁有 AFN 大部分常用功能访得,包括網(wǎng)絡(luò)狀態(tài)監(jiān)聽等龙亲,提供類方法和實(shí)例方法調(diào)用。
根據(jù) JavaWeb 中 HttpServlet 的設(shè)計(jì)思想悍抑,把網(wǎng)絡(luò)請(qǐng)求的回調(diào)封裝成了 request 和 response 兩個(gè)對(duì)象鳄炉。
支持界面多級(jí)緩存。
支持鏈?zhǔn)秸{(diào)用搜骡。
支持配合 RAC 框架使用鏈?zhǔn)秸{(diào)用拂盯,支持自定義信號(hào)的返回值。
安裝
支持 Cocoapods 安裝
pod 'DKNetworking'
或者下載 Demo 項(xiàng)目
將根目錄下的 DKNetworking 和 Vendor 文件夾拉進(jìn)項(xiàng)目记靡,其中 Vendor 文件夾有 AFNetworking谈竿、MJExtension、YYCache 和 ReactiveCocoa摸吠,前三個(gè)是必須依賴的空凸,ReactiveCocoa 是可選的。
必須在 Target -> Build Phases -> Link Binary With Libraries 中添加
libsqlite3.0.tbd
寸痢,這是 YYCache 框架所需的呀洲。
使用
絕大部分方法都可以直接看 DKNetworking.h 中的聲明以及注釋。
獲取單例對(duì)象
DKNetworking *networking = [DKNetworking networkManager];
從 1.1.0 版本開始提供了一個(gè)單例對(duì)象宏 DKNetworkManager
啼止。
DKN 配置
設(shè)置請(qǐng)求根路徑
[DKNetworking setupBaseURL:@"https://m.sfddj.com/app/v1/"];
baseURL 的路徑一定要有“/”結(jié)尾道逗,設(shè)置后所有的網(wǎng)絡(luò)訪問都使用相對(duì)路徑。
設(shè)置日志
開啟日志打印
[DKNetworking openLog];
關(guān)閉日志打印
[DKNetworking closeLog];
設(shè)置緩存
設(shè)置緩存方式
/**
設(shè)置緩存方式
DKNetworkCacheTypeNetworkOnly : 只加載網(wǎng)絡(luò)數(shù)據(jù)
DKNetworkCacheTypeCacheNetwork : 先加載緩存,然后加載網(wǎng)絡(luò)
@param cacheType 緩存類型
*/
+ (void)setupCacheType:(DKNetworkCacheType)cacheType;
獲取緩存大小
/**
獲取網(wǎng)絡(luò)緩存的總大小 動(dòng)態(tài)單位(GB,MB,KB,B)
@return 網(wǎng)絡(luò)緩存的總大小
*/
+ (NSString *)cacheSize;
清除緩存
[DKNetworkCache clearCache];
設(shè)置序列化格式
設(shè)置請(qǐng)求序列化格式
/**
設(shè)置網(wǎng)絡(luò)請(qǐng)求參數(shù)的格式 : 默認(rèn)為二進(jìn)制格式
@param requestSerializer DKRequestSerializerJSON:JSON格式, DKRequestSerializerHTTP:二進(jìn)制格式
*/
+ (void)setRequestSerializer:(DKRequestSerializer)requestSerializer;
設(shè)置響應(yīng)反序列化格式
/**
設(shè)置服務(wù)器響應(yīng)數(shù)據(jù)格式 : 默認(rèn)為JSON格式
@param responseSerializer DKResponseSerializerJSON:JSON格式, DKResponseSerializerHTTP:二進(jìn)制格式
*/
+ (void)setResponseSerializer:(DKResponseSerializer)responseSerializer;
設(shè)置請(qǐng)求超時(shí)時(shí)間
/**
設(shè)置請(qǐng)求超時(shí)時(shí)間 : 默認(rèn)10秒
@param time 請(qǐng)求超時(shí)時(shí)長(zhǎng)(秒)
*/
+ (void)setRequestTimeoutInterval:(NSTimeInterval)time;
設(shè)置 Header
設(shè)置一對(duì)請(qǐng)求頭參數(shù)
/**
設(shè)置一對(duì)請(qǐng)求頭參數(shù)
@param value 請(qǐng)求頭參數(shù)值
@param field 請(qǐng)求頭參數(shù)名
*/
+ (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
設(shè)置多對(duì)請(qǐng)求頭參數(shù)
/**
設(shè)置多對(duì)請(qǐng)求頭參數(shù)
@param networkHeader 請(qǐng)求頭參數(shù)字典
*/
+ (void)setNetworkHeader:(NSDictionary *)networkHeader;
網(wǎng)絡(luò)狀態(tài)
監(jiān)聽網(wǎng)絡(luò)狀態(tài)
[DKNetworking networkStatusWithBlock:^(DKNetworkStatus status) {
switch (status) {
case DKNetworkStatusUnknown: // 網(wǎng)絡(luò)狀態(tài)未知
break;
case DKNetworkStatusNotReachable: // 無網(wǎng)絡(luò)
break;
case DKNetworkStatusReachableViaWWAN: // 手機(jī)網(wǎng)絡(luò)(蜂窩)
break;
case DKNetworkStatusReachableViaWiFi: // WIFI網(wǎng)絡(luò)
break;
}
}];
獲取當(dāng)前網(wǎng)絡(luò)狀態(tài)
/**
有網(wǎng)絡(luò):YES, 無網(wǎng)絡(luò):NO
*/
+ (BOOL)isNetworking;
/**
手機(jī)網(wǎng)絡(luò):YES, 非手機(jī)網(wǎng)絡(luò):NO
*/
+ (BOOL)isWWANNetwork;
/**
WiFi網(wǎng)絡(luò):YES, 非WiFi網(wǎng)絡(luò):NO
*/
+ (BOOL)isWiFiNetwork;
網(wǎng)絡(luò)請(qǐng)求
常規(guī)調(diào)用
以 POST 方法為例族壳,方法定義:
/**
POST請(qǐng)求
@param URL 請(qǐng)求地址
@param parameters 請(qǐng)求參數(shù)
@param callback 請(qǐng)求回調(diào)
@return 返回的對(duì)象可取消請(qǐng)求,調(diào)用cancel方法
*/
+ (NSURLSessionTask *)POST:(NSString *)URL parameters:(NSDictionary *)parameters callback:(DKNetworkBlock)callback;
- (NSURLSessionTask *)POST:(NSString *)URL parameters:(NSDictionary *)parameters callback:(DKNetworkBlock)callback;
調(diào)用:
[DKNetworking POST:url parameters:@{@"name":@"bingo"} callback:^(DKNetworkRequest *request, DKNetworkResponse *response) {
// ...
}];
鏈?zhǔn)秸{(diào)用
方法定義:
/** 鏈?zhǔn)秸{(diào)用 */
- (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;
調(diào)用:
DKNetworkManager.post(url).params(@{@"name":@"bingo"}).callback(^(DKNetworkRequest *request, DKNetworkResponse *response) {
// ...
});
RAC 鏈?zhǔn)秸{(diào)用
方法定義:
在原鏈?zhǔn)椒椒ǖ幕A(chǔ)上補(bǔ)充了一個(gè) executeSignal 方法憔辫,返回信號(hào)。
#ifdef RAC
/** RAC鏈?zhǔn)桨l(fā)送請(qǐng)求 */
- (RACSignal *)executeSignal;
#endif
調(diào)用:
[DKNetworkManager.post(url).params(@{@"name":@"bingo"}).executeSignal subscribeNext:^(RACTuple *x) {
DKNetworkResponse *response = x.second;
// ...
} error:^(NSError *error) {
// ...
}];
RAC 鏈?zhǔn)秸{(diào)用 自定義信號(hào)的返回值
方法定義:
#ifdef RAC
/**
設(shè)置響應(yīng)結(jié)果回調(diào)仿荆,可以設(shè)置信號(hào)返回的value為自己想要的值,比如用MJExtension框架坏平,將DKNetworkResponse對(duì)象的rawData字典轉(zhuǎn)為自己項(xiàng)目的實(shí)體類再返回
@param flattenMapBlock 結(jié)果映射的設(shè)置回調(diào)block拢操,其中RACTuple的first為DKNetworkRequest對(duì)象,second為DKNetworkResponse對(duì)象
*/
+ (void)setupResponseSignalWithFlattenMapBlock:(DKNetworkFlattenMapBlock)flattenMapBlock;
#endif
使用:
考慮到代碼執(zhí)行順序的問題舶替,建議在項(xiàng)目中創(chuàng)建一個(gè)繼承自 NSObject 的類 DKNetworkConfig令境,在 + load 方法中調(diào)用 DKN 的配置方法,設(shè)置回調(diào)的信號(hào)的 return 值顾瞪,根據(jù)不同項(xiàng)目進(jìn)行配置舔庶。
[DKNetworking setupResponseSignalWithFlattenMapBlock:^RACStream *(RACTuple *tuple) {
DKNetworkResponse *response = tuple.second; // 框架默認(rèn)返回的response
MyHttpResponse *myResponse = [MyHttpResponse mj_objectWithKeyValues:response.rawData]; // 項(xiàng)目需要的response
myResponse.rawData = response.rawData;
myResponse.error = response.error;
return [RACSignal return:RACTuplePack(tuple.first, myResponse)];
}];
調(diào)用:
經(jīng)過上面的setupResponseSignalWithFlattenMapBlock:
方法設(shè)置后抛蚁,信號(hào)返回的 RACTuple 的 second 為自定義的 response 對(duì)象。
[DKNetworkManager.post(url).executeSignal subscribeNext:^(RACTuple *x) {
// DKNetworkResponse *response = x.second;
MyHttpResponse *myResponse = x.second;
// ...
} error:^(NSError *error) {
// ...
}];
取消請(qǐng)求
/**
取消所有HTTP請(qǐng)求
*/
+ (void)cancelAllRequest;
/**
取消指定URL的HTTP請(qǐng)求
*/
+ (void)cancelRequestWithURL:(NSString *)URL;
上傳
上傳文件
/**
上傳文件
@param URL 請(qǐng)求地址
@param parameters 請(qǐng)求參數(shù)
@param name 文件對(duì)應(yīng)服務(wù)器上的字段
@param filePath 文件本地的沙盒路徑
@param progressBlock 上傳進(jìn)度回調(diào)
@param callback 請(qǐng)求回調(diào)
@return 返回的對(duì)象可取消請(qǐng)求,調(diào)用cancel方法
*/
+ (NSURLSessionTask *)uploadFileWithURL:(NSString *)URL
parameters:(NSDictionary *)parameters
name:(NSString *)name
filePath:(NSString *)filePath
progressBlock:(DKNetworkProgressBlock)progressBlock
callback:(void(^)(DKNetworkResponse *response))callback;
上傳圖片
/**
上傳圖片
@param URL 請(qǐng)求地址
@param parameters 請(qǐng)求參數(shù)
@param name 圖片對(duì)應(yīng)服務(wù)器上的字段
@param images 圖片數(shù)組
@param fileNames 圖片文件名數(shù)組惕橙,傳入nil時(shí)數(shù)組內(nèi)的文件名默認(rèn)為當(dāng)前日期時(shí)間戳+索引
@param imageScale 圖片文件壓縮比 范圍 (0.f ~ 1.f)
@param imageType 圖片文件的類型瞧甩,例:png、jpg(默認(rèn)類型)....
@param progressBlock 上傳進(jìn)度回調(diào)
@param callback 請(qǐng)求回調(diào)
@return 返回的對(duì)象可取消請(qǐng)求,調(diào)用cancel方法
*/
+ (NSURLSessionTask *)uploadImagesWithURL:(NSString *)URL
parameters:(NSDictionary *)parameters
name:(NSString *)name
images:(NSArray<UIImage *> *)images
fileNames:(NSArray<NSString *> *)fileNames
imageScale:(CGFloat)imageScale
imageType:(NSString *)imageType
progressBlock:(DKNetworkProgressBlock)progressBlock
callback:(void(^)(DKNetworkResponse *response))callback;
下載
/**
下載文件
@param URL 請(qǐng)求地址
@param fileDir 文件存儲(chǔ)目錄(默認(rèn)存儲(chǔ)目錄為Download)
@param progressBlock 文件下載的進(jìn)度回調(diào)
@param callback 請(qǐng)求回調(diào)弥鹦,filePath為文件保存路徑
@return 返回 NSURLSessionDownloadTask 實(shí)例肚逸,可用于暫停繼續(xù),暫停調(diào)用 suspend 方法彬坏,開始下載調(diào)用 resume 方法
*/
+ (NSURLSessionTask *)downloadWithURL:(NSString *)URL
fileDir:(NSString *)fileDir
progressBlock:(DKNetworkProgressBlock)progressBlock
callback:(void(^)(NSString *filePath, NSError *error))callback;
TODO
鏈?zhǔn)骄幊屉m然好看一點(diǎn)朦促,但是沒有常規(guī)調(diào)用方便,不能像以往一樣敲兩個(gè)字母 Xcode 就智能補(bǔ)全整個(gè)方法栓始,并且可能會(huì)出現(xiàn)
.get().post().get().post()
這種沒有意義的鏈?zhǔn)秸{(diào)用的情況务冕。用面向協(xié)議(接口)的函數(shù)式編程可以解決上面的問題,但是網(wǎng)絡(luò)層工具的調(diào)用是可以沒有順序的幻赚,比如
.params().post()
跟.post().params()
應(yīng)該是都可以的洒疚,這樣的話會(huì)讓協(xié)議很難制定,還是會(huì)出現(xiàn)上面的問題坯屿。日志保存文件
考慮添加 UI 層分類油湖,讓網(wǎng)絡(luò)請(qǐng)求的錯(cuò)誤日志以一種更友好的方式展示。
與服務(wù)端的通訊加密领跛,考慮添加 RSA 非對(duì)稱加密等方法乏德。
后話
DKNetworking 框架源碼已經(jīng)托管在 GitHub,遵循 MIT 協(xié)議吠昭。歡迎向我們發(fā)起 PR 貢獻(xiàn)您的代碼喊括,或者有什么建議歡迎 Issues。如果對(duì)您有幫助矢棚,請(qǐng)給予 star郑什,你們的支持是我奮斗的動(dòng)力,謝謝蒲肋!