XMNetworking 網絡庫的設計與使用

XMNetwoking 是咱們團隊開源的一個網絡庫,詳見:GitHubphp

XMNetworking 是一個輕量的、簡單易用但功能強大的網絡庫,基於 AFNetworking 3.0 封裝。
其中,XM 前綴是咱們團隊 Xcode-Men 的縮寫。英文文檔ios

簡介

XMNetworking.pnggit

如上圖所示,XMNetworking 採用中心化的設計思想,由 XMCenter 統一發起並管理全部的 XMRequest 請求,並可經過 XMCenter 給全部請求配置回調線程、公共 Server URL、Header、Parameter 等信息,同時也能夠 Block 注入的方式實現自定義的響應結果處理邏輯,如數據模型轉換、業務錯誤碼判斷、網絡緩存等。另外增長了 XMEgine 這一層是爲了隔離底層第三方庫依賴,便於之後切換其餘底層網絡庫或本身實現底層邏輯。github

特性

  • 簡單易用,發送請求只需調用一個方法,經過 Block 配置信息,代碼緊湊;
  • 功能強大,適用於幾乎全部的網絡請求使用場景(普通請求、上傳、下載);
  • 專爲 RESTful Server API 設計,並提供多種不一樣的請求和響應的序列化類型;
  • 支持批量請求、鏈式請求等複雜業務邏輯的網絡需求;
  • 可隨時取消未完成的網絡請求,支持自動重試失敗的請求;
  • 全局配置全部請求的公共信息,自定義回調線程以及響應處理邏輯;
  • 支持檢查網絡鏈接類型,並集成 AFNetworking 強大的安全策略模塊。

系統要求

  • iOS 7.0 以上系統
  • Xcode 7.3 或更高版本

安裝說明

CocoaPods

在你工程的 Podfile 文件中添加以下一行,並執行 pod installpod updateapi

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 拖拽到你的工程中。session

注意: XMNetworking 已經包含了 AFNetworking 3.1.0 的源代碼,因此你無需經過 Carthage 生成 AFNetworking.framework 導到你工程中,不然會有衝突!

手動安裝

下載 XMNetworking 子文件夾的全部內容,並把其中的源文件添加(拖放)到你的工程中。

使用教程

頭文件的導入

  • 若是是經過 CocoaPods 或 Carthage 安裝,則:
#import <XMNetworking/XMNetworking.h>
  • 若是是手動下載源碼安裝,則:
#import "XMNetworking.h"

全局網絡配置

[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,且其 useGeneralServerYES(默認),那麼該請求的服務端地址 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 地址,但當 serverapiurl 三個屬性被同時賦值時,url 的優先級比較高,而此時 serverapi 的值會被忽略。

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.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 等,使用方式與上述相似,再也不贅述。

詳見 XMConstXMRequestXMCenter 等幾個文件中的代碼和註釋。

上傳請求

// `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 中有兩個屬性 requestSerializerTyperesponseSerializerType 分別用於設置請求參數和響應結果的序列化類型。

其中,XMRequestSerializerTypeXMResponseSerializerType 枚舉的定義以下:

typedef NS_ENUM(NSInteger, XMRequestSerializerType) {
    kXMRequestSerializerRAW     = 0, // default
    kXMRequestSerializerJSON    = 1,
    kXMRequestSerializerPlist   = 2,
};
typedef NS_ENUM(NSInteger, XMResponseSerializerType) {
    kXMResponseSerializerRAW    = 0,
    kXMResponseSerializerJSON   = 1, // default
    kXMResponseSerializerPlist  = 2,
    kXMResponseSerializerXML    = 3,
};

詳見 AFURLRequestSerialization.hAFURLResponseSerialization.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 中的 responseObjectserrors 中元素的順序與每一個 XMRequest 對象在 batchRequest.requestArray 中的順序一致。

[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<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 中的 responseObjectserrors 中元素的順序與每一個鏈式請求 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,XMRequestXMCenterXMEngine 則是核心類的聲明和實現,具體的代碼結構以下圖所示:

Architecture.png

待完善

  • 支持斷點下載
  • 支持網絡層緩存
  • 兼容測試支持 tvOS/watchOS/OS X
  • 更增強大的自定義模型轉換
  • 實現一套可擴展的插件機制,便於 XMNetworking 增長新功能

做者

貢獻者

許可證

XMNetworking 使用 MIT 許可證,詳情見 LICENSE 文件。


 

文/XcodeMen(簡書做者) 原文連接:http://www.jianshu.com/p/a5c5e9aa5913

相關文章
相關標籤/搜索