XMNetworking 网络库的设计与使用
2019獨角獸企業重金招聘Python工程師標準>>>
XMNetwoking 是我們團隊開源的一個網絡庫,詳見:GitHub
XMNetworking 是一個輕量的、簡單易用但功能強大的網絡庫,基于 AFNetworking 3.0 封裝。
其中,XM 前綴是我們團隊 Xcode-Men 的縮寫。英文文檔
簡介
XMNetworking.png
如上圖所示,XMNetworking 采用中心化的設計思想,由 XMCenter 統一發起并管理所有的 XMRequest 請求,并可通過 XMCenter 給所有請求配置回調線程、公共 Server URL、Header、Parameter 等信息,同時也可以 Block 注入的方式實現自定義的響應結果處理邏輯,如數據模型轉換、業務錯誤碼判斷、網絡緩存等。另外增加了 XMEgine 這一層是為了隔離底層第三方庫依賴,便于以后切換其他底層網絡庫或自己實現底層邏輯。
特性
- 簡單易用,發送請求只需調用一個方法,通過 Block 配置信息,代碼緊湊;
- 功能強大,適用于幾乎所有的網絡請求使用場景(普通請求、上傳、下載);
- 專為 RESTful Server API 設計,并提供多種不同的請求和響應的序列化類型;
- 支持批量請求、鏈式請求等復雜業務邏輯的網絡需求;
- 可隨時取消未完成的網絡請求,支持自動重試失敗的請求;
- 全局配置所有請求的公共信息,自定義回調線程以及響應處理邏輯;
- 支持檢查網絡連接類型,并集成 AFNetworking 強大的安全策略模塊。
系統要求
- iOS 7.0 以上系統
- Xcode 7.3 或更高版本
安裝說明
CocoaPods
在你工程的 Podfile 文件中添加如下一行,并執行 pod install 或 pod update。
pod 'XMNetworking'注意: XMNetworking 已經包含了 AFNetworking 3.1.0 的源代碼,所以你工程里的 Podfile 文件不能再添加 pod AFNetworking 去導入 AFNetworking,否則會有沖突!
Carthage (只支持 iOS 8+)
與 CocoaPods 不同的是,Carthage 是一個去中心化的第三方依賴庫管理工具,它自動幫你編譯所依賴的第三方庫并以 framework 形式提供給你。
你可以通過 Homebrew 執行以下命令來安裝 Carthage:
$ brew update $ brew install carthage成功安裝完 Carthage 后,在你工程的 Cartfile 文件中添加如下一行:
github "kangzubin/XMNetworking"然后執行 carthage update --platform ios 命令生成 framework 包,并把生成的 XMNetworking.framework 拖拽到你的工程中。
注意: XMNetworking 已經包含了 AFNetworking 3.1.0 的源代碼,所以你無需通過 Carthage 生成 AFNetworking.framework 導到你工程中,否則會有沖突!
手動安裝
下載 XMNetworking 子文件夾的所有內容,并把其中的源文件添加(拖放)到你的工程中。
使用教程
頭文件的導入
- 如果是通過 CocoaPods 或 Carthage 安裝,則:
- 如果是手動下載源碼安裝,則:
全局網絡配置
[XMCenter setupConfig:^(XMConfig *config) {config.generalServer = @"general server address";config.generalHeaders = @{@"general-header": @"general header value"};config.generalParameters = @{@"general-parameter": @"general parameter value"};config.generalUserInfo = nil;config.callbackQueue = dispatch_get_main_queue(); #ifdef DEBUGconfig.consoleLog = YES; #endif }];你可以調用 XMCenter 的 +setupConfig: 類方法,通過修改傳入的 XMConfig 對象來配置全局網絡請求的公共信息,包括如下:
- generalServer: 公共服務端地址,如果一個 XMRequest 請求對象的 server 屬性為 nil,且其 useGeneralServer 為 YES(默認),那么該請求的服務端地址 server 將會取 XMCenter 中 generalServer 的值。
- generalParameters: 公共請求參數,如果一個 XMRequest 請求對象的 useGeneralParameters 屬性為 YES(默認),并且 XMCenter 的公共參數 generalParameters 不為空,那么這些公共參數會自動加到該請求的 parameters 中。
- generalHeaders: 公共請求頭,如果一個 XMRequest 請求對象的 useGeneralHeaders 屬性為 YES(默認),并且 XMCenter 的公共請求頭 generalHeaders 不為空,那么這些公共請求頭會自動加到該請求的 headers 中。
- generalUserInfo: 公共用戶信息,默認為 nil,如果一個 XMRequest 請求對象的 userInfo 屬性為 nil(默認)而該字段不為 nil,那么該字段會自動賦值給 XMRequest 對象的 userInfo。而 userInfo 屬性可用于區分具有相同上下文信息的不同請求。
- callbackQueue: 請求的回調 Block 執行的 dispatch 隊列(線程),如果為 NULL(默認),那么會在一個私有的并發隊列(子線程)中執行回調 Block。
- consoleLog: 一個 BOOL 值,用于表示是否在控制臺輸出請求和響應的信息,默認為 NO。
另外,你可以通過調用 XMCenter 的以下兩個類方法來隨時修改全局公共的 header 和 parameter:
+ (void)setGeneralHeaderValue:(nullable NSString *)value forField:(NSString *)field; + (void)setGeneralParameterValue:(nullable NSString *)value forKey:(NSString *)key;普通請求
GET
[XMCenter sendRequest:^(XMRequest *request) {request.url = @"http://example.com/v1/foo/bar";//request.server = @"http://example.com/v1/";//request.api = @"foo/bar";request.parameters = @{@"param1": @"value1", @"param2": @"value2"};request.headers = @{@"User-Agent": @"Custom User Agent"};request.httpMethod = kXMHTTPMethodGET; } onSuccess:^(id responseObject) {NSLog(@"onSuccess: %@", responseObject); } onFailure:^(NSError *error) {NSLog(@"onFailure: %@", error); } onFinished:^(id responseObject, NSError *error) {NSLog(@"onFinished"); }];注意1:可以通過以下兩種方法設置一個請求對象的 URL 地址,但當 server、api 和 url 三個屬性被同時賦值時,url 的優先級比較高,而此時 server、api 的值會被忽略。
request.url = @"http://example.com/v1/foo/bar"; // 如果 request.server 為 `nil`,且 request.useGeneralServer 為 `YES`,那么此時 request.server 會取 XMCenter.generalServer 的值。 request.server = @"http://example.com/v1/"; request.api = @"foo/bar";注意2:一個請求對象的回調 Block (success/failure/finished/progress) 是非必需的(默認為 nil),XMCenter 提供了多個設置不同回調 Block 參數的方法用于發送請求。另外,需要注意的是,success/faillure/finished 等回調 Block 會在 XMCenter 設置的 callbackQueue 隊列中被執行,但 progress 回調 Block 將在 NSURLSession 自己的隊列中執行,而不是 callbackQueue。
POST
[XMCenter sendRequest:^(XMRequest *request) {//request.server = @"http://example.com/v1/"; // 可選,如果為空則讀取 XMCenter.generalServerrequest.api = @"foo/bar";request.parameters = @{@"param1": @"value1", @"param2": @"value2"};request.httpMethod = kXMHTTPMethodPOST; // 可選,默認為 `POST`request.requestType = kXMRequestNormal; // 可選,默認為 `Normal` } onSuccess:^(id responseObject) {NSLog(@"onSuccess: %@", responseObject); } onFailure:^(NSError *error) {NSLog(@"onFailure: %@", error); }];其他 HTTP 方法
XMRequest 同樣支持其他 HTTP 方法,比如:HEAD, DELETE, PUT, PATCH 等,使用方式與上述類似,不再贅述。
詳見 XMConst、XMRequest 和 XMCenter 等幾個文件中的代碼和注釋。
上傳請求
// `NSData` form data. UIImage *image = [UIImage imageNamed:@"testImage"]; NSData *fileData1 = UIImageJPEGRepresentation(image, 1.0); // `NSURL` form data. NSString *path = [NSHomeDirectory() stringByAppendingString:@"/Documents/testImage.png"]; NSURL *fileURL2 = [NSURL fileURLWithPath:path isDirectory:NO];[XMCenter sendRequest:^(XMRequest *request) {request.server = @"http://example.com/v1/";request.api = @"foo/bar";request.requestType = kXMRequestUpload;[request addFormDataWithName:@"image[]" fileName:@"temp.jpg" mimeType:@"image/jpeg" fileData:fileData1];[request addFormDataWithName:@"image[]" fileURL:fileURL2];// see `XMUploadFormData` for more details. } onProgress:^(NSProgress *progress) {// the progress block is running on the session queue.if (progress) {NSLog(@"onProgress: %f", progress.fractionCompleted);} } onSuccess:^(id responseObject) {NSLog(@"onSuccess: %@", responseObject); } onFailure:^(NSError *error) {NSLog(@"onFailure: %@", error); } onFinished:^(id responseObject, NSError *error) {NSLog(@"onFinished"); }];下載請求
[XMCenter sendRequest:^(XMRequest *request) {request.url = @"http://example.com/v1/testDownFile.zip";request.downloadSavePath = [NSHomeDirectory() stringByAppendingString:@"/Documents/"];request.requestType = kXMRequestDownload; } onProgress:^(NSProgress *progress) {// the progress block is running on the session queue.if (progress) {NSLog(@"onProgress: %f", progress.fractionCompleted);} } onSuccess:^(id responseObject) {NSLog(@"onSuccess: %@", responseObject); } onFailure:^(NSError *error) {NSLog(@"onFailure: %@", error); }];序列化
XMRequest 中有兩個屬性 requestSerializerType 和 responseSerializerType 分別用于設置請求參數和響應結果的序列化類型。
其中,XMRequestSerializerType 和 XMResponseSerializerType 枚舉的定義如下:
typedef NS_ENUM(NSInteger, XMRequestSerializerType) {kXMRequestSerializerRAW = 0, // defaultkXMRequestSerializerJSON = 1,kXMRequestSerializerPlist = 2, }; typedef NS_ENUM(NSInteger, XMResponseSerializerType) {kXMResponseSerializerRAW = 0,kXMResponseSerializerJSON = 1, // defaultkXMResponseSerializerPlist = 2,kXMResponseSerializerXML = 3, };詳見 AFURLRequestSerialization.h 和 AFURLResponseSerialization.h 獲取更多細節。
自定義響應結果的處理邏輯
通常地,一個請求成功結束時,會執行 success block,當有錯誤發生時,執行 failure block。然而,開發中更常見的情況是,即使是一個請求成功結束,我們也需要進一步處理,比如驗證響應結果數據、判斷與服務端商量好的業務錯誤碼類型等,再決定執行 success block 還是 failure block。
現在,你可以調用 [XMCenter setResponseProcessBlock:...] 方法以 Block 注入的方式設置自定義的處理邏輯,當請求成功結束時,這個 Block 會在 success block 被執行前調用,如果傳入 *error 參數被賦值,則接下來會執行 failure block。
[XMCenter setResponseProcessBlock:^(XMRequest *request, id responseObject, NSError *__autoreleasing *error) {// 自定義響應結果處理邏輯,如果 `*error` 被賦值,則接下來會執行 failure block。 }];批量請求
XMNetworking 支持同時發一組批量請求,這組請求在業務邏輯上相關,但請求本身是互相獨立的,success block 會在所有請求都成功結束時才執行,而一旦有一個請求失敗,則會執行 failure block。注:回調 Block 中的 responseObjects 和 errors 中元素的順序與每個 XMRequest 對象在 batchRequest.requestArray 中的順序一致。
[XMCenter sendBatchRequest:^(XMBatchRequest *batchRequest) {XMRequest *request1 = [XMRequest request];request1.url = @"server url 1";// set other properties for request1XMRequest *request2 = [XMRequest request];request2.url = @"server url 2";// set other properties for request2[batchRequest.requestArray addObject:request1];[batchRequest.requestArray addObject:request2]; } onSuccess:^(NSArray<id> *responseObjects) {NSLog(@"onSuccess: %@", responseObjects); } onFailure:^(NSArray<id> *errors) {NSLog(@"onFailure: %@", errors); } onFinished:^(NSArray<id> *responseObjects, NSArray<id> *errors) {NSLog(@"onFinished"); }];[XMCenter sendBatchRequest:...] 方法會返回剛發起的新的 XMBatchRequest 對象,你可以保存這個對象,并在必要的時候調用它的 -cancelWithBlock: 方法取消這組批量請求。
鏈式請求
XMNetworking 同樣支持發一組鏈式請求,這組請求之間互相依賴,下一請求是否發送以及請求的參數取決于上一個請求的結果,success block 會在所有的鏈式請求都成功結束時才執行,而中間一旦有一個請求失敗,則會執行 failure block。注:回調 Block 中的 responseObjects 和 errors 中元素的順序與每個鏈式請求 XMRequest 對象的先后順序一致。
[XMCenter sendChainRequest:^(XMChainRequest *chainRequest) {[[[[chainRequest onFirst:^(XMRequest *request) {request.url = @"server url 1";// set other properties for request}] onNext:^(XMRequest *request, id responseObject, BOOL *sendNext) {NSDictionary *params = responseObject;if (params.count > 0) {request.url = @"server url 2";request.parameters = params;} else {*sendNext = NO;}}] onNext:^(XMRequest *request, id responseObject, BOOL *sendNext) {request.url = @"server url 3";request.parameters = @{@"param1": @"value1", @"param2": @"value2"};}] onNext: ...]; } onSuccess:^(NSArray<id> *responseObjects) {NSLog(@"onSuccess: %@", responseObjects); } onFailure:^(NSArray<id> *errors) {NSLog(@"onFailure: %@", errors); } onFinished:^(NSArray<id> *responseObjects, NSArray<id> *errors) {NSLog(@"onFinished"); }];[XMCenter sendChainRequest:...] 方法會返回剛發起的新的 XMChainRequest 對象,你可以保存這個對象,并在必要的時候調用它的 -cancelWithBlock: 方法取消這組鏈式請求。
取消一個網絡請求
當調用 [XMCenter sendRequest:...] 方法發送一個網絡請求時,該方法會返回一個用于唯一標識該請求對象的 identifier(如果請求發送失敗,該值為 0)。在必要的時候,你可以通過這個 identifier 來取消當前網絡請求(如果一個請求已經結束,這時再用 identifier 來取消該請求時,會直接忽略)。
// send a request NSUInteger identifier = [XMCenter sendRequest:^(XMRequest *request) {request.server = @"https://kangzubin.cn/";request.api = @"test/index.php";request.httpMethod = kXMHTTPMethodGET;request.timeoutInterval = 10;request.retryCount = 1; } onFailure:^(NSError *error) {NSLog(@"onFailure: %@", error); }];// your business code sleep(2);// cancel the running request by identifier with cancel block [XMCenter cancelRequest:identifier onCancel:^(XMRequest *request) {NSLog(@"onCancel"); }];注意:調用 XMCenter cancelRequest:onCancel: 方法取消一個網絡請求時,被取消的請求對象(如果存在)會以參數的形式傳給 cancel block,另外 cancel block 是在當前調用 cancelRequest: 方法的線程中執行,并不是 XMCenter 的 callbackQueue。
網絡可連接性檢查
我們提供了兩種方法用于獲取網絡的可連接性,分別如下:
[XMCenter isNetworkReachable]; // 該方法會返回一個 Bool 值用于表示當前網絡是否可連接。 [[XMEngine sharedEngine] networkReachability]; // 該方法會返回一個當前網絡的狀態值,-1 表示 `Unknown`,0 表示 `NotReachable,1 表示 `WWAN`,2 表示 `WiFi`詳見 AFNetworkReachabilityManager 獲取更多細節.
HTTPS 請求的本地證書校驗(SSL Pinning)
在你的應用程序包里添加 (pinned) 相應的 SSL 證書做校驗有助于防止中間人攻擊和其他安全漏洞。非常方便的是,AFNetworking 的 AFSecurityPolicy 安全模塊可以通過校驗本地保存的證書或公鑰幫助我們評估服務器是否可信任以及建立安全連接。
我們在 XMEngine 中暴露了一個 AFHTTPSessionManager 對象叫 sessionManager,你可以通過修改該對象的 securityPolicy 類型,以開啟 SSL Pinning 功能,并把你們服務器對應的 .cer 證書或者公鑰放到你的工程中。
[XMEngine sharedEngine].sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];詳見 AFSecurityPolicy 獲取更多細節.
文檔
詳見 XMNetworking Documents Link.
結構
XMNetworking 的代碼結構非常簡潔和緊湊,只包含了 4 個核心文件:XMConst.h 用于定義全局常量枚舉和 Block,XMRequest,XMCenter 和 XMEngine 則是核心類的聲明和實現,具體的代碼結構如下圖所示:
Architecture.png
待完善
- 支持斷點下載
- 支持網絡層緩存
- 兼容測試支持 tvOS/watchOS/OS X
- 更加強大的自定義模型轉換
- 實現一套可擴展的插件機制,便于 XMNetworking 增加新功能
作者
- Zubin Kang
貢獻者
- southpeak
- Xcode-Men Team
許可證
XMNetworking 使用 MIT 許可證,詳情見 LICENSE 文件。
?
文/XcodeMen(簡書作者)
原文鏈接:http://www.jianshu.com/p/a5c5e9aa5913
轉載于:https://my.oschina.net/u/2345393/blog/810373
總結
以上是生活随笔為你收集整理的XMNetworking 网络库的设计与使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: API 类和面向对象简介
- 下一篇: webParts与Web部件