iOS網(wǎng)絡(luò)請求相關(guān)框架的使用
關(guān)于iOS相關(guān)技術(shù)的博客非常非常多了,沒有好的內(nèi)容也不想寫,最近在迭代公司項目版本,對于這個題材也想了很久,看了很多類似的文章,決定記錄一下。網(wǎng)絡(luò)請求,是客戶端開發(fā)中一個很重要的模塊,關(guān)于此方面需要了解的東西也是非常多的,此篇文章僅介紹自己用過的有關(guān)框架。
AFNetworking
1. 關(guān)于AFNetworking
只要是做iOS開發(fā)的應(yīng)該都是知道這個框架的,有多優(yōu)秀我就不贅述了。自iOS9之后蘋果棄用了NSURLConnection只用NSURLSession,所以AFN從3.0版本開始就刪除了基于NSURLConnection API的所有支持,基于NSURLSession框架以及NSOperation進行的封裝開發(fā)。
2. 基于AFNetworking進行網(wǎng)絡(luò)請求
既然用的是第三方框架,那么肯定會有一些局限性。框架的迭代更新都會影響我們的代碼,所以盡可能解耦,一般我們都會單獨的寫一個網(wǎng)絡(luò)請求工具類對框架進行封裝。這樣即使框架更改了,也只需要更改工具類相關(guān)代碼。
a.新建網(wǎng)絡(luò)請求工具類,實例化AFHTTPSessionManager。類似如下 :
- + (instancetype)sharedInstance
- {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _mutParamares = [NSMutableDictionary dictionary];
- _manager = [AFHTTPSessionManager manager];
- _manager.requestSerializer = [AFHTTPRequestSerializer serializer];
- _manager.requestSerializer.timeoutInterval = 30.0f;
- [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
- _manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json",
- @"text/plain", @"text/javascript", @"text/xml", @"image/*", nil];
- });
- }
兩個需要注意的問題:***,這里的網(wǎng)絡(luò)請求工具類是一個單利,為什么要用單利呢? [AFHTTPSessionManager manager]跟蹤到這個方法里,會看到返回的manager并不是單利,如果每次請求都實例化一個manager的話,那么有可能造成內(nèi)存泄漏。第二,有時候請求失敗的原因是AFN支持的response類型和服務(wù)器返回給我們的類型不一致,需要修改AFN的源碼進行修改,但用Cocoapods來管理三方框架,pod update之后修改的代碼又會被重置。此時,就可以通過acceptableContentTypes屬性來根據(jù)需要設(shè)置。
- + (instancetype)manager {
- return [[self alloc] initWithBaseURL:nil];
- }
也可以通過requestSerializer屬性設(shè)置請求頭相關(guān)的信息。如:
- [self.manager.requestSerializer setValue:@"" forHTTPHeaderField:@""];
b.常用的網(wǎng)絡(luò)請求類型。這是對外的API,外部通過調(diào)用這些接口實現(xiàn)相關(guān)的網(wǎng)絡(luò)請求。當(dāng)然你也可以根據(jù)自己的需要暴露相關(guān)的API。相應(yīng)的接口實現(xiàn)比較簡單。具體的邏輯還應(yīng)根據(jù)業(yè)務(wù)需求在外部實現(xiàn)。對于文件的操作,如圖片的上傳,可參考代碼如下。
- image.png
- // 上傳多張圖片
- [_manager POST:url parameters:param constructingBodyWithBlock:^(id<afmultipartformdata> _Nonnull formData) {
- for (UIImage *image in imgArray) {
- UIImage *resizeImage = image.reSizeImage;
- NSData *data = UIImagePNGRepresentation(resizeImage);
- [formData appendPartWithFileData:data name: @"file" fileName:[NSString stringWithFormat:@"img%ld.png",i] mimeType:@"image/png"];
- }];</afmultipartformdata>
上傳多張圖片的時候,根據(jù)需要可對圖片進行裁剪和壓縮。關(guān)于上傳的進度可以通過progressBlock返回在對應(yīng)的UI上進行顯示,進度的計算公式如下。
- processBlock(progress.completedUnitCount / progress.totalUnitCount);
c.監(jiān)測網(wǎng)絡(luò)狀態(tài)。
- AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
- [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
- switch (status) {
- case AFNetworkReachabilityStatusUnknown: break;
- case AFNetworkReachabilityStatusNotReachable: break;
- case AFNetworkReachabilityStatusReachableViaWWAN: break;
- case AFNetworkReachabilityStatusReachableViaWiFi: break;
- }
- }];
- [manager startMonitoring];
以上是基于AFNetworking的網(wǎng)絡(luò)請求。上面說的這種網(wǎng)絡(luò)請求方式是集約式的網(wǎng)絡(luò)請求,也就是所有的API都調(diào)用的是這個工具類。還有一種網(wǎng)絡(luò)請求方式是離散式的,也就是每一個API都有自己對應(yīng)的類。
YTKNetwork
1. 關(guān)于YTKNetwork
這個框架也是基于AFNetworking進行的再次封裝,適用于規(guī)模較大的項目中。YTKNetwork是離散式的網(wǎng)絡(luò)請求方式,如上所述,每一個請求都對應(yīng)一個對象,可根據(jù)需要對相應(yīng)的請求進行定制化。另外YTKNetwork支持批量網(wǎng)絡(luò)請求發(fā)送并設(shè)置統(tǒng)一回調(diào)、支持相互依賴的網(wǎng)絡(luò)請求等等功能。
2. 基于YTKNetwork進行網(wǎng)絡(luò)請求
a. 同上,不建議直接使用第三方,自己寫一個BaseRequest類繼承YTKRequest,在這個類里面實現(xiàn)下面這個方法。這個方法是所有請求的Response。
- - (void)startWithCompletionBlockWithSuccess:(YTKRequestCompletionBlock)success failure:(YTKRequestCompletionBlock)failure{}
b. 寫一個網(wǎng)絡(luò)請求配置類。在程序啟動的時候通過YTKNetworkConfig配置網(wǎng)絡(luò)請求。如baseUrl參數(shù)等。通過YTKNetworkAgent設(shè)置一些參數(shù),如上面提到的acceptableContentTypes參數(shù)。這個類是真正發(fā)起請求的類,也是在這個類中與AFN打交道。
- _config = [YTKNetworkConfig sharedConfig];
- _config.baseUrl = BASE_INTERFACE_URL_DEV;
- YTKNetworkAgent *agent = [YTKNetworkAgent sharedAgent];
- NSSet *acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/plain", @"text/html", @"text/css", nil];
- NSString *keypath = @"jsonResponseSerializer.acceptableContentTypes";
- [agent setValue:acceptableContentTypes forKeyPath:keypath];
3.以一個具體的API(請求用戶信息)請求為例。
3.1 新建一個UserInfoAPI類,繼承你寫的BaseRequest類。
3.2 實現(xiàn)以下方法。請求的URL,和外部無關(guān),不需要外面?zhèn)鬟M來。請求的類型,以及請求的參數(shù)。當(dāng)然,解析也可以在API類內(nèi)部實現(xiàn)。通過在GET方法里面實現(xiàn)數(shù)據(jù)的轉(zhuǎn)換和解析。
- - (NSString *)requestUrl {
- return kUserURL;
- }
- - (YTKRequestMethod)requestMethod {
- return YTKRequestMethodPOST;
- }
- - (instancetype)requestArgument {
- return parameter;
- }
serverRespData是基類自定義的一個參數(shù),是數(shù)據(jù)過濾之后的response。
- _serverRespData = [self.responseJSONObject objectForKey:@"data"];
- - (QDZQUseModel *)user { // 重寫user的get方法。
- _user = [QDZQUserEntity yy_modelWithDictionary:[self.serverRespData objectForKey:@"appUser"]];
- return _user;
- }
3.3 如何調(diào)用這個API
- + (void)fetchUserInfoSuccess:(void (^)(void))success failure:(void (^)(NSError * error))failure {
- UserInfoApi *api = [[UserInfoApi alloc] init];
- [api startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) {
- if (success) { }
- } failure:^(__kindof YTKBaseRequest * _Nonnull request) {
- if (failure) { }
- }];
- }
3.4 添加請求頭
如果你需要添加請求頭的話,你可以實現(xiàn)下面這個方法。
- - (nullable NSDictionary<nsstring *, nsstring *> *)requestHeaderFieldValueDictionary {
- return @{@"token" : @""};
- }</nsstring *, nsstring *>
以上,是兩種網(wǎng)絡(luò)請求方式(離散式、集約式)。