XMNetwoking 是我們團隊開源的一個網路庫,詳見:GitHub
XMNetworking 是一個輕量的、簡單易用但功能強大的網路庫,基於 AFNetworking 3.0 封裝。
其中,XM
字首是我們團隊 Xcode-Men 的縮寫。英文文件
簡介
如上圖所示,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
。
1 |
pod 'XMNetworking' |
注意: XMNetworking
已經包含了 AFNetworking
3.1.0 的原始碼,所以你工程裡的 Podfile
檔案不能再新增 pod AFNetworking
去匯入 AFNetworking
,否則會有衝突!
Carthage (只支援 iOS 8+)
與 CocoaPods 不同的是,Carthage 是一個去中心化的第三方依賴庫管理工具,它自動幫你編譯所依賴的第三方庫並以 framework 形式提供給你。
你可以通過 Homebrew 執行以下命令來安裝 Carthage:
1 2 |
$ brew update $ brew install carthage |
成功安裝完 Carthage 後,在你工程的 Cartfile
檔案中新增如下一行:
1 |
github "kangzubin/XMNetworking" |
然後執行 carthage update --platform ios
命令生成 framework 包,並把生成的 XMNetworking.framework
拖拽到你的工程中。
注意: XMNetworking
已經包含了 AFNetworking
3.1.0 的原始碼,所以你無需通過 Carthage 生成 AFNetworking.framework
導到你工程中,否則會有衝突!
手動安裝
下載 XMNetworking
子資料夾的所有內容,並把其中的原始檔新增(拖放)到你的工程中。
使用教程
標頭檔案的匯入
- 如果是通過 CocoaPods 或 Carthage 安裝,則:
1 |
#import |
- 如果是手動下載原始碼安裝,則:
1 |
#import "XMNetworking.h" |
全域性網路配置
1 2 3 4 5 6 7 8 9 10 |
[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 DEBUG config.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:
1 2 |
+ (void)setGeneralHeaderValue:(nullable NSString *)value forField:(NSString *)field; + (void)setGeneralParameterValue:(nullable NSString *)value forKey:(NSString *)key; |
普通請求
GET
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[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
的值會被忽略。
1 |
request.url = @"http://example.com/v1/foo/bar"; |
1 2 3 |
// 如果 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
1 2 3 4 5 6 7 8 9 10 11 |
[XMCenter sendRequest:^(XMRequest *request) { //request.server = @"http://example.com/v1/"; // 可選,如果讀取 XMCenter.generalServer request.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
等幾個檔案中的程式碼和註釋。
上傳請求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// `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"); }]; |
下載請求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[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
列舉的定義如下:
1 2 3 4 5 |
typedef NS_ENUM(NSInteger, XMRequestSerializerType) { kXMRequestSerializerRAW = 0, // default kXMRequestSerializerJSON = 1, kXMRequestSerializerPlist = 2, }; |
1 2 3 4 5 6 |
typedef NS_ENUM(NSInteger, XMResponseSerializerType) { kXMResponseSerializerRAW = 0, kXMResponseSerializerJSON = 1, // default kXMResponseSerializerPlist = 2, kXMResponseSerializerXML = 3, }; |
詳見 AFURLRequestSerialization.h
和 AFURLResponseSerialization.h
獲取更多細節。
自定義響應結果的處理邏輯
通常地,一個請求成功結束時,會執行 success block,當有錯誤發生時,執行 failure block。然而,開發中更常見的情況是,即使是一個請求成功結束,我們也需要進一步處理,比如驗證響應結果資料、判斷與服務端商量好的業務錯誤碼型別等,再決定執行 success block 還是 failure block。
現在,你可以呼叫 [XMCenter setResponseProcessBlock:...]
方法以 Block 注入的方式設定自定義的處理邏輯,當請求成功結束時,這個 Block 會在 success block 被執行前呼叫,如果傳入 *error
引數被賦值,則接下來會執行 failure block。
1 2 3 |
[XMCenter setResponseProcessBlock:^(XMRequest *request, id responseObject, NSError *__autoreleasing *error) { // 自定義響應結果處理邏輯,如果 `*error` 被賦值,則接下來會執行 failure block。 }]; |
批量請求
XMNetworking 支援同時發一組批量請求,這組請求在業務邏輯上相關,但請求本身是互相獨立的,success block 會在所有請求都成功結束時才執行,而一旦有一個請求失敗,則會執行 failure block。注:回撥 Block 中的 responseObjects
和 errors
中元素的順序與每個 XMRequest 物件在 batchRequest.requestArray
中的順序一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[XMCenter sendBatchRequest:^(XMBatchRequest *batchRequest) { XMRequest *request1 = [XMRequest request]; request1.url = @"server url 1"; // set other properties for request1 XMRequest *request2 = [XMRequest request]; request2.url = @"server url 2"; // set other properties for request2 [batchRequest.requestArray addObject:request1]; [batchRequest.requestArray addObject:request2]; } onSuccess:^(NSArray *responseObjects) { NSLog(@"onSuccess: %@", responseObjects); } onFailure:^(NSArray *errors) { NSLog(@"onFailure: %@", errors); } onFinished:^(NSArray *responseObjects, NSArray *errors) { NSLog(@"onFinished"); }]; |
[XMCenter sendBatchRequest:...]
方法會返回剛發起的新的 XMBatchRequest
物件,你可以儲存這個物件,並在必要的時候呼叫它的 -cancelWithBlock:
方法取消這組批量請求。
鏈式請求
XMNetworking 同樣支援發一組鏈式請求,這組請求之間互相依賴,下一請求是否傳送以及請求的引數取決於上一個請求的結果,success block 會在所有的鏈式請求都成功結束時才執行,而中間一旦有一個請求失敗,則會執行 failure block。注:回撥 Block 中的 responseObjects
和 errors
中元素的順序與每個鏈式請求 XMRequest
物件的先後順序一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
[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 *responseObjects) { NSLog(@"onSuccess: %@", responseObjects); } onFailure:^(NSArray *errors) { NSLog(@"onFailure: %@", errors); } onFinished:^(NSArray *responseObjects, NSArray *errors) { NSLog(@"onFinished"); }]; |
[XMCenter sendChainRequest:...]
方法會返回剛發起的新的 XMChainRequest
物件,你可以儲存這個物件,並在必要的時候呼叫它的 -cancelWithBlock:
方法取消這組鏈式請求。
取消一個網路請求
當呼叫 [XMCenter sendRequest:...]
方法傳送一個網路請求時,該方法會返回一個用於唯一標識該請求物件的 identifier
(如果請求傳送失敗,該值為 0
)。在必要的時候,你可以通過這個 identifier
來取消當前網路請求(如果一個請求已經結束,這時再用 identifier
來取消該請求時,會直接忽略)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 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
。
網路可連線性檢查
我們提供了兩種方法用於獲取網路的可連線性,分別如下:
1 2 |
[XMCenter isNetworkReachable]; // 該方法會返回一個 Bool 值用於表示當前網路是否可連線。 |
1 2 |
[[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
證照或者公鑰放到你的工程中。
1 |
[XMEngine sharedEngine].sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; |
詳見 AFSecurityPolicy
獲取更多細節.
文件
詳見 XMNetworking Documents Link.
結構
XMNetworking 的程式碼結構非常簡潔和緊湊,只包含了 4 個核心檔案:XMConst.h
用於定義全域性常量列舉和 Block,XMRequest
,XMCenter
和 XMEngine
則是核心類的宣告和實現,具體的程式碼結構如下圖所示:
待完善
- 支援斷點下載
- 支援網路層快取
- 相容測試支援 tvOS/watchOS/OS X
- 更加強大的自定義模型轉換
- 實現一套可擴充套件的外掛機制,便於 XMNetworking 增加新功能
作者
貢獻者
許可證
XMNetworking 使用 MIT 許可證,詳情見 LICENSE 檔案。