XMNetwoking 是我們團(tuán)隊(duì)開源的一個(gè)網(wǎng)絡(luò)庫,詳見:GitHub
XMNetworking 是一個(gè)輕量的搏屑、簡(jiǎn)單易用但功能強(qiáng)大的網(wǎng)絡(luò)庫,基于 AFNetworking 3.0 封裝。
其中糠雨,XM
前綴是我們團(tuán)隊(duì) Xcode-Men 的縮寫。英文文檔
簡(jiǎn)介
如上圖所示徘跪,XMNetworking 采用中心化的設(shè)計(jì)思想甘邀,由 XMCenter
統(tǒng)一發(fā)起并管理所有的 XMRequest
請(qǐng)求,并可通過 XMCenter
給所有請(qǐng)求配置回調(diào)線程垮庐、公共 Server URL松邪、Header、Parameter 等信息哨查,同時(shí)也可以 Block 注入的方式實(shí)現(xiàn)自定義的響應(yīng)結(jié)果處理邏輯逗抑,如數(shù)據(jù)模型轉(zhuǎn)換、業(yè)務(wù)錯(cuò)誤碼判斷寒亥、網(wǎng)絡(luò)緩存等邮府。另外增加了 XMEgine
這一層是為了隔離底層第三方庫依賴,便于以后切換其他底層網(wǎng)絡(luò)庫或自己實(shí)現(xiàn)底層邏輯溉奕。
特性
- 簡(jiǎn)單易用挟纱,發(fā)送請(qǐng)求只需調(diào)用一個(gè)方法,通過 Block 配置信息腐宋,代碼緊湊紊服;
- 功能強(qiáng)大,適用于幾乎所有的網(wǎng)絡(luò)請(qǐng)求使用場(chǎng)景(普通請(qǐng)求胸竞、上傳欺嗤、下載);
- 專為 RESTful Server API 設(shè)計(jì)卫枝,并提供多種不同的請(qǐng)求和響應(yīng)的序列化類型煎饼;
- 支持批量請(qǐng)求、鏈?zhǔn)秸?qǐng)求等復(fù)雜業(yè)務(wù)邏輯的網(wǎng)絡(luò)需求;
- 可隨時(shí)取消未完成的網(wǎng)絡(luò)請(qǐng)求校赤,支持自動(dòng)重試失敗的請(qǐng)求吆玖;
- 全局配置所有請(qǐng)求的公共信息筒溃,自定義回調(diào)線程以及響應(yīng)處理邏輯;
- 支持檢查網(wǎng)絡(luò)連接類型沾乘,并集成 AFNetworking 強(qiáng)大的安全策略模塊怜奖。
系統(tǒng)要求
- iOS 7.0 以上系統(tǒng)
- Xcode 7.3 或更高版本
安裝說明
CocoaPods
在你工程的 Podfile
文件中添加如下一行,并執(zhí)行 pod install
或 pod update
翅阵。
pod 'XMNetworking'
注意: XMNetworking
已經(jīng)包含了 AFNetworking
3.1.0 的源代碼歪玲,所以你工程里的 Podfile
文件不能再添加 pod AFNetworking
去導(dǎo)入 AFNetworking
,否則會(huì)有沖突掷匠!
Carthage (只支持 iOS 8+)
與 CocoaPods 不同的是滥崩,Carthage 是一個(gè)去中心化的第三方依賴庫管理工具,它自動(dòng)幫你編譯所依賴的第三方庫并以 framework 形式提供給你讹语。
你可以通過 Homebrew 執(zhí)行以下命令來安裝 Carthage:
$ brew update
$ brew install carthage
成功安裝完 Carthage 后钙皮,在你工程的 Cartfile
文件中添加如下一行:
github "kangzubin/XMNetworking"
然后執(zhí)行 carthage update --platform ios
命令生成 framework 包,并把生成的 XMNetworking.framework
拖拽到你的工程中顽决。
注意: XMNetworking
已經(jīng)包含了 AFNetworking
3.1.0 的源代碼短条,所以你無需通過 Carthage 生成 AFNetworking.framework
導(dǎo)到你工程中,否則會(huì)有沖突擎值!
手動(dòng)安裝
下載 XMNetworking
子文件夾的所有內(nèi)容慌烧,并把其中的源文件添加(拖放)到你的工程中。
使用教程
頭文件的導(dǎo)入
- 如果是通過 CocoaPods 或 Carthage 安裝鸠儿,則:
#import <XMNetworking/XMNetworking.h>
- 如果是手動(dòng)下載源碼安裝屹蚊,則:
#import "XMNetworking.h"
全局網(wǎng)絡(luò)配置
[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
}];
你可以調(diào)用 XMCenter
的 +setupConfig:
類方法,通過修改傳入的 XMConfig
對(duì)象來配置全局網(wǎng)絡(luò)請(qǐng)求的公共信息进每,包括如下:
-
generalServer: 公共服務(wù)端地址汹粤,如果一個(gè) XMRequest 請(qǐng)求對(duì)象的
server
屬性為nil
,且其useGeneralServer
為YES
(默認(rèn))田晚,那么該請(qǐng)求的服務(wù)端地址server
將會(huì)取 XMCenter 中generalServer
的值嘱兼。 -
generalParameters: 公共請(qǐng)求參數(shù),如果一個(gè) XMRequest 請(qǐng)求對(duì)象的
useGeneralParameters
屬性為YES
(默認(rèn))贤徒,并且 XMCenter 的公共參數(shù)generalParameters
不為空芹壕,那么這些公共參數(shù)會(huì)自動(dòng)加到該請(qǐng)求的parameters
中。 -
generalHeaders: 公共請(qǐng)求頭接奈,如果一個(gè) XMRequest 請(qǐng)求對(duì)象的
useGeneralHeaders
屬性為YES
(默認(rèn))踢涌,并且 XMCenter 的公共請(qǐng)求頭generalHeaders
不為空,那么這些公共請(qǐng)求頭會(huì)自動(dòng)加到該請(qǐng)求的headers
中序宦。 -
generalUserInfo: 公共用戶信息睁壁,默認(rèn)為
nil
,如果一個(gè) XMRequest 請(qǐng)求對(duì)象的userInfo
屬性為nil
(默認(rèn))而該字段不為nil
,那么該字段會(huì)自動(dòng)賦值給XMRequest
對(duì)象的userInfo
潘明。而userInfo
屬性可用于區(qū)分具有相同上下文信息的不同請(qǐng)求行剂。 -
callbackQueue: 請(qǐng)求的回調(diào) Block 執(zhí)行的 dispatch 隊(duì)列(線程),如果為
NULL
(默認(rèn))钳降,那么會(huì)在一個(gè)私有的并發(fā)隊(duì)列(子線程)中執(zhí)行回調(diào) Block厚宰。 -
consoleLog: 一個(gè)
BOOL
值,用于表示是否在控制臺(tái)輸出請(qǐng)求和響應(yīng)的信息牲阁,默認(rèn)為NO
固阁。
另外壤躲,你可以通過調(diào)用 XMCenter
的以下兩個(gè)類方法來隨時(shí)修改全局公共的 header 和 parameter:
+ (void)setGeneralHeaderValue:(nullable NSString *)value forField:(NSString *)field;
+ (void)setGeneralParameterValue:(nullable NSString *)value forKey:(NSString *)key;
普通請(qǐng)求
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:可以通過以下兩種方法設(shè)置一個(gè)請(qǐng)求對(duì)象的 URL 地址城菊,但當(dāng) server
、api
和 url
三個(gè)屬性被同時(shí)賦值時(shí)碉克,url
的優(yōu)先級(jí)比較高凌唬,而此時(shí) server
、api
的值會(huì)被忽略漏麦。
request.url = @"http://example.com/v1/foo/bar";
// 如果 request.server 為 `nil`客税,且 request.useGeneralServer 為 `YES`,那么此時(shí) request.server 會(huì)取 XMCenter.generalServer 的值撕贞。
request.server = @"http://example.com/v1/";
request.api = @"foo/bar";
注意2:一個(gè)請(qǐng)求對(duì)象的回調(diào) Block (success/failure/finished/progress) 是非必需的(默認(rèn)為 nil
)更耻,XMCenter 提供了多個(gè)設(shè)置不同回調(diào) Block 參數(shù)的方法用于發(fā)送請(qǐng)求。另外捏膨,需要注意的是秧均,success/faillure/finished 等回調(diào) Block 會(huì)在 XMCenter 設(shè)置的 callbackQueue
隊(duì)列中被執(zhí)行,但 progress 回調(diào) Block 將在 NSURLSession 自己的隊(duì)列中執(zhí)行号涯,而不是 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; // 可選链快,默認(rèn)為 `POST`
request.requestType = kXMRequestNormal; // 可選誉己,默認(rèn)為 `Normal`
} onSuccess:^(id responseObject) {
NSLog(@"onSuccess: %@", responseObject);
} onFailure:^(NSError *error) {
NSLog(@"onFailure: %@", error);
}];
其他 HTTP 方法
XMRequest 同樣支持其他 HTTP 方法,比如:HEAD
, DELETE
, PUT
, PATCH
等域蜗,使用方式與上述類似巨双,不再贅述。
詳見 XMConst
霉祸、XMRequest
和 XMCenter
等幾個(gè)文件中的代碼和注釋筑累。
上傳請(qǐng)求
// `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");
}];
下載請(qǐng)求
[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
中有兩個(gè)屬性 requestSerializerType
和 responseSerializerType
分別用于設(shè)置請(qǐng)求參數(shù)和響應(yīng)結(jié)果的序列化類型。
其中脉执,XMRequestSerializerType
和 XMResponseSerializerType
枚舉的定義如下:
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.h
和 AFURLResponseSerialization.h
獲取更多細(xì)節(jié)疼阔。
自定義響應(yīng)結(jié)果的處理邏輯
通常地,一個(gè)請(qǐng)求成功結(jié)束時(shí),會(huì)執(zhí)行 success block婆廊,當(dāng)有錯(cuò)誤發(fā)生時(shí)迅细,執(zhí)行 failure block。然而淘邻,開發(fā)中更常見的情況是茵典,即使是一個(gè)請(qǐng)求成功結(jié)束,我們也需要進(jìn)一步處理宾舅,比如驗(yàn)證響應(yīng)結(jié)果數(shù)據(jù)统阿、判斷與服務(wù)端商量好的業(yè)務(wù)錯(cuò)誤碼類型等,再?zèng)Q定執(zhí)行 success block 還是 failure block筹我。
現(xiàn)在扶平,你可以調(diào)用 [XMCenter setResponseProcessBlock:...]
方法以 Block 注入的方式設(shè)置自定義的處理邏輯,當(dāng)請(qǐng)求成功結(jié)束時(shí)蔬蕊,這個(gè) Block 會(huì)在 success block 被執(zhí)行前調(diào)用结澄,如果傳入 *error
參數(shù)被賦值,則接下來會(huì)執(zhí)行 failure block岸夯。
[XMCenter setResponseProcessBlock:^(XMRequest *request, id responseObject, NSError *__autoreleasing *error) {
// 自定義響應(yīng)結(jié)果處理邏輯麻献,如果 `*error` 被賦值,則接下來會(huì)執(zhí)行 failure block猜扮。
}];
批量請(qǐng)求
XMNetworking 支持同時(shí)發(fā)一組批量請(qǐng)求勉吻,這組請(qǐng)求在業(yè)務(wù)邏輯上相關(guān),但請(qǐng)求本身是互相獨(dú)立的旅赢,success block 會(huì)在所有請(qǐng)求都成功結(jié)束時(shí)才執(zhí)行齿桃,而一旦有一個(gè)請(qǐng)求失敗,則會(huì)執(zhí)行 failure block鲜漩。注:回調(diào) Block 中的 responseObjects
和 errors
中元素的順序與每個(gè) XMRequest 對(duì)象在 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:...]
方法會(huì)返回剛發(fā)起的新的 XMBatchRequest
對(duì)象,你可以保存這個(gè)對(duì)象孕似,并在必要的時(shí)候調(diào)用它的 -cancelWithBlock:
方法取消這組批量請(qǐng)求踩娘。
鏈?zhǔn)秸?qǐng)求
XMNetworking 同樣支持發(fā)一組鏈?zhǔn)秸?qǐng)求,這組請(qǐng)求之間互相依賴喉祭,下一請(qǐng)求是否發(fā)送以及請(qǐng)求的參數(shù)取決于上一個(gè)請(qǐng)求的結(jié)果养渴,success block 會(huì)在所有的鏈?zhǔn)秸?qǐng)求都成功結(jié)束時(shí)才執(zhí)行,而中間一旦有一個(gè)請(qǐng)求失敗泛烙,則會(huì)執(zhí)行 failure block理卑。注:回調(diào) Block 中的 responseObjects
和 errors
中元素的順序與每個(gè)鏈?zhǔn)秸?qǐng)求 XMRequest
對(duì)象的先后順序一致。
[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:...]
方法會(huì)返回剛發(fā)起的新的 XMChainRequest
對(duì)象蔽氨,你可以保存這個(gè)對(duì)象藐唠,并在必要的時(shí)候調(diào)用它的 -cancelWithBlock:
方法取消這組鏈?zhǔn)秸?qǐng)求帆疟。
取消一個(gè)網(wǎng)絡(luò)請(qǐng)求
當(dāng)調(diào)用 [XMCenter sendRequest:...]
方法發(fā)送一個(gè)網(wǎng)絡(luò)請(qǐng)求時(shí),該方法會(huì)返回一個(gè)用于唯一標(biāo)識(shí)該請(qǐng)求對(duì)象的 identifier
(如果請(qǐng)求發(fā)送失敗宇立,該值為 0
)踪宠。在必要的時(shí)候,你可以通過這個(gè) identifier
來取消當(dāng)前網(wǎng)絡(luò)請(qǐng)求(如果一個(gè)請(qǐng)求已經(jīng)結(jié)束妈嘹,這時(shí)再用 identifier
來取消該請(qǐng)求時(shí)柳琢,會(huì)直接忽略)。
// 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");
}];
注意:調(diào)用 XMCenter cancelRequest:onCancel:
方法取消一個(gè)網(wǎng)絡(luò)請(qǐng)求時(shí)润脸,被取消的請(qǐng)求對(duì)象(如果存在)會(huì)以參數(shù)的形式傳給 cancel block柬脸,另外 cancel block 是在當(dāng)前調(diào)用 cancelRequest:
方法的線程中執(zhí)行,并不是 XMCenter 的 callbackQueue
毙驯。
網(wǎng)絡(luò)可連接性檢查
我們提供了兩種方法用于獲取網(wǎng)絡(luò)的可連接性倒堕,分別如下:
[XMCenter isNetworkReachable];
// 該方法會(huì)返回一個(gè) Bool 值用于表示當(dāng)前網(wǎng)絡(luò)是否可連接。
[[XMEngine sharedEngine] networkReachability];
// 該方法會(huì)返回一個(gè)當(dāng)前網(wǎng)絡(luò)的狀態(tài)值尔苦,-1 表示 `Unknown`涩馆,0 表示 `NotReachable行施,1 表示 `WWAN`允坚,2 表示 `WiFi`
詳見 AFNetworkReachabilityManager
獲取更多細(xì)節(jié).
HTTPS 請(qǐng)求的本地證書校驗(yàn)(SSL Pinning)
在你的應(yīng)用程序包里添加 (pinned) 相應(yīng)的 SSL 證書做校驗(yàn)有助于防止中間人攻擊和其他安全漏洞。非常方便的是蛾号,AFNetworking 的 AFSecurityPolicy
安全模塊可以通過校驗(yàn)本地保存的證書或公鑰幫助我們?cè)u(píng)估服務(wù)器是否可信任以及建立安全連接稠项。
我們?cè)?XMEngine
中暴露了一個(gè) AFHTTPSessionManager
對(duì)象叫 sessionManager
,你可以通過修改該對(duì)象的 securityPolicy
類型鲜结,以開啟 SSL Pinning 功能展运,并把你們服務(wù)器對(duì)應(yīng)的 .cer
證書或者公鑰放到你的工程中。
[XMEngine sharedEngine].sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
詳見 AFSecurityPolicy
獲取更多細(xì)節(jié).
文檔
詳見 XMNetworking Documents Link.
結(jié)構(gòu)
XMNetworking 的代碼結(jié)構(gòu)非常簡(jiǎn)潔和緊湊精刷,只包含了 4 個(gè)核心文件:XMConst.h
用于定義全局常量枚舉和 Block拗胜,XMRequest
,XMCenter
和 XMEngine
則是核心類的聲明和實(shí)現(xiàn)怒允,具體的代碼結(jié)構(gòu)如下圖所示:
待完善
- 支持?jǐn)帱c(diǎn)下載
- 支持網(wǎng)絡(luò)層緩存
- 兼容測(cè)試支持 tvOS/watchOS/OS X
- 更加強(qiáng)大的自定義模型轉(zhuǎn)換
- 實(shí)現(xiàn)一套可擴(kuò)展的插件機(jī)制埂软,便于 XMNetworking 增加新功能
作者
貢獻(xiàn)者
許可證
XMNetworking 使用 MIT 許可證,詳情見 LICENSE 文件纫事。