Swift3.0 + AFN3.0 + MJExtention + (泛型)封裝網(wǎng)絡(luò)請(qǐng)求類

寫在前面:對(duì)于移動(dòng)端開發(fā)來說,無論iOS,Android,抑或是H5開發(fā),所謂的開發(fā)核心之一就是從服務(wù)端接收數(shù)據(jù),前端負(fù)責(zé)展示,所以,當(dāng)然還有各種邏輯的實(shí)現(xiàn),但是呢,一個(gè)完善的APP肯定是少不了與服務(wù)端交互的,所以一個(gè)網(wǎng)絡(luò)請(qǐng)求類的封裝好用與否直接關(guān)系到一個(gè)項(xiàng)目的開發(fā)效率以及后期的維護(hù),本人也接手過那種沒有請(qǐng)求類的項(xiàng)目呢,每個(gè)請(qǐng)求寫了一堆代碼,用的全部是ASI,,然后需要更改為AFN的時(shí)候,哇,簡直無敵了,然后Command+F都用不上,只能一處處找,找的自己都不知道時(shí)間了,真的相當(dāng)折磨,受過這個(gè)折磨的童鞋肯定能夠體會(huì)這種痛苦,有種刪除項(xiàng)目,拍屁股走人的邪惡想法,但是呢,畢竟咱的職業(yè)素養(yǎng)還在,所以就耐心的享受這折磨了.廢話不多說,相信每個(gè)開發(fā)的童鞋都能知道一個(gè)好的網(wǎng)絡(luò)請(qǐng)求類帶給我們的便捷之處.

** 1.Swift3.0**

不用過多介紹什么,Swift是蘋果主推的開發(fā)語言,老司機(jī)都知道原來是OC,Swift語言筆者前天花了一下午+一晚上的時(shí)間學(xué)習(xí)了下,確實(shí)很簡潔,開發(fā)速度確實(shí)很快,風(fēng)格類似于(JS+Python)我比較喜歡的是Swift中的泛型,泛型這個(gè)東西Java中有,當(dāng)初寫Java的時(shí)候泛型真的讓我方便了很多很多,所以在進(jìn)行iOS開發(fā)的時(shí)候一直琢磨著有沒有泛型這個(gè)東西,可是呢,都知道沒有...最近安卓新出的kotlin語言,跟Swift也是大同小異吧.

** 2.AFN**

AFN是一套iOS網(wǎng)絡(luò)請(qǐng)求框架,用OC語言編寫(我記得原來有Swift語言編寫的,但是去GitHub上找的時(shí)候沒有找到)這個(gè)框架現(xiàn)在幾乎所有的iOS項(xiàng)目都在用吧,確實(shí)很好用,當(dāng)然不缺乏大牛公司自己封裝的,當(dāng)然也可能有老項(xiàng)目還在用ASI(曾經(jīng)火了好長時(shí)間,但是現(xiàn)在沒人用了,WHy?因?yàn)樽髡卟桓铝?..可悲不)

** 3.MJExtention**

MJExtention是李明杰大神的一套框架,主要用來字典轉(zhuǎn)模型,這個(gè)框架的好用程度本人給100分,當(dāng)然還有別的功能,其他的童鞋們慢慢去挖掘吧.即便你的對(duì)象里包含對(duì)象數(shù)組,它也可以幫你全部轉(zhuǎn)換的哦,有不懂的或者有興趣的可以直接去GitHub上下載學(xué)習(xí)一下,或者給我留言也可以哦.

4.封裝思想

我也相信這些封裝思想應(yīng)該很多人都知道了,思想就像風(fēng)暴一樣,瞬間可以席卷全球,真是個(gè)神奇的東西,當(dāng)然我的這個(gè)也是來源于網(wǎng)絡(luò)上大神的分享.
AFN的源碼我看了一遍,我們可以直接用AFHTTPSessionManager,有興趣的童鞋可以研究下AFN的源碼,然后我的想法是繼承AFHTTPSessionManager創(chuàng)建一個(gè)RequsetManager類,(當(dāng)然第一版我用的是裝飾模式,是在RequsetManager中裝飾一個(gè)AFHTTPSessionManager對(duì)象,當(dāng)然兩種方法各有各的優(yōu)點(diǎn),直接繼承可能調(diào)用方法上更加簡潔一點(diǎn),然后也會(huì)減少一個(gè)對(duì)象的使用...)提供一個(gè)RequsetManager單例,然后創(chuàng)建一個(gè)NetworkService類,為所有的請(qǐng)求提供服務(wù),在這個(gè)里面使用包裝RequsetManager,使用RequsetManager下載數(shù)據(jù),在NetworkService中提供對(duì)數(shù)據(jù)的解密,緩存(當(dāng)然我沒有去實(shí)現(xiàn)緩存的功能),以及對(duì)數(shù)據(jù)的解析轉(zhuǎn)換(MJExtention在這兒真的起了很大的作用--在此轉(zhuǎn)換數(shù)據(jù)的時(shí)候我們需要知道將json數(shù)據(jù)轉(zhuǎn)換為什么樣的對(duì)象,在OC版本中實(shí)現(xiàn)的時(shí)候我們是將要轉(zhuǎn)換對(duì)象的Class當(dāng)做參數(shù)傳了過去,但是返回的結(jié)果是id的,我們用的時(shí)候需要進(jìn)行強(qiáng)轉(zhuǎn),但是在Swift中我們有了泛型,所以這一步就簡潔了很多,具體的祥看代碼,后面會(huì)同樣奉上OC版本的封裝).

5.OC版本

RequsetManager:僅僅實(shí)現(xiàn)了Post和GET方法,聲明了成功和失敗的Block回調(diào),一個(gè)重新載入請(qǐng)求頭的,當(dāng)然大部分是沒有用的,另外一個(gè)是獲取單利的,但是,所有的方法均是靜態(tài)方法,獲取單利是可以在外部單獨(dú)設(shè)置請(qǐng)求頭


#import "AFHTTPSessionManager.h"

typedef void (^requestSuccessBlock)(id responseObj);
typedef void (^requestFailureBlock) (NSError *error);

@interface RequsetManager : AFHTTPSessionManager
+ (instancetype)sharedRequestManager;
+ (void)AFN_ReloadHeaderAuth;
+ (void)AFN_GetRequest:(NSString *)url params:(NSDictionary *)params success:(requestSuccessBlock)successHandler failure:(requestFailureBlock)failureHandler;
+ (void)AFN_PostRequest:(NSString *)url params:(NSDictionary *)params success:(requestSuccessBlock)successHandler failure:(requestFailureBlock)failureHandler;
@end

實(shí)現(xiàn)如下:

#import "RequsetManager.h"
@interface RequsetManager()
@end
@implementation RequsetManager
+ (instancetype)sharedRequestManager{
    
    static dispatch_once_t onceToken;
    static RequsetManager * manager = nil;
    dispatch_once(&onceToken, ^{
        manager = [[self alloc]init];
        manager.requestSerializer.timeoutInterval = 60.0f;
        [manager.requestSerializer setValue:@"" forHTTPHeaderField:@"jwttoken"];
        manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    });
    return manager;
}

+ (void)AFN_ReloadHeaderAuth{
    [[[self sharedRequestManager] requestSerializer] setValue:@"" forHTTPHeaderField:@"jwttoken"];
}

+ (void)AFN_GetRequest:(NSString *)url params:(NSDictionary *)params success:(requestSuccessBlock)successHandler failure:(requestFailureBlock)failureHandler{
    [[self sharedRequestManager] GET:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        successHandler(responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        failureHandler(error);
    }];
}

+ (void)AFN_PostRequest:(NSString *)url params:(NSDictionary *)params success:(requestSuccessBlock)successHandler failure:(requestFailureBlock)failureHandler{
    [[self sharedRequestManager] POST:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        successHandler(responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        failureHandler(error);
    }];

}

@end

RequestService:僅僅封裝了返回結(jié)果為JSON的,其實(shí)有XMLDictionary可以將XML轉(zhuǎn)換為JSON的,有興趣的可以從后面我的GitHub上下載demo ,里面包含了這個(gè)類,當(dāng)然也可以直接從GitHub上搜索

#import <Foundation/Foundation.h>
typedef void (^responseResultBlock)(id dataObj, NSError *error);
@interface RequestService : NSObject

/**
 重新載入請(qǐng)求頭
 */
+ (void)reloadAFNHeaderAuth;

/**
 利用AFN請(qǐng)求獲取JSON返回 --- GET請(qǐng)求

 @param url 請(qǐng)求地址
 @param param 請(qǐng)求參數(shù)
 @param modelClass 請(qǐng)求返回所需要轉(zhuǎn)換的模型類
 @param responseBlock 請(qǐng)求成功|失敗回調(diào)
 */
+ (void)AFN_JSONResponseGetWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock) responseBlock;

/**
 利用AFN請(qǐng)求獲取JSON返回 --- POST請(qǐng)求
 
 @param url 請(qǐng)求地址
 @param param 請(qǐng)求參數(shù)
 @param modelClass 請(qǐng)求返回所需要轉(zhuǎn)換的模型類
 @param responseBlock 請(qǐng)求成功|失敗回調(diào)
 */
+ (void)AFN_JSONResponsePostWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock)responseBlock;


/**
 請(qǐng)求返回結(jié)果轉(zhuǎn)換為模型方法  -- 此方法不需要關(guān)注,如果有Service繼承此類,可以重寫該方法進(jìn)行數(shù)據(jù)處理

 @param responseObj 返回結(jié)果
 @param modelClass 模型類
 @return 轉(zhuǎn)換成功的模型
 */
+ (id)modelTransformationWithResponseObj:(id)responseObj modelClass:(Class)modelClass;

@end

具體實(shí)現(xiàn)如下:

#import "RequestService.h"
#import "RequsetManager.h"
#import "MJExtension.h"

static id dataObj;
@implementation RequestService

+ (void)reloadAFNHeaderAuth{
    [RequsetManager AFN_ReloadHeaderAuth];
}
+ (void)AFN_JSONResponseGetWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock) responseBlock{
    [RequsetManager AFN_GetRequest:url params:param success:^(id responseObj) {
        dataObj = [self modelTransformationWithResponseObj:responseObj modelClass:modelClass];
        responseBlock(dataObj,nil);
    } failure:^(NSError *error) {
        responseBlock(nil,error);
    }];
}
+ (void)AFN_JSONResponsePostWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock)responseBlock{
    [RequsetManager AFN_PostRequest:url params:param success:^(id responseObj) {
        dataObj = [self modelTransformationWithResponseObj:responseObj modelClass:modelClass];
        responseBlock(dataObj,nil);
    } failure:^(NSError *error) {
        responseBlock(nil,error);
    }];
}


+ (id)convertJson:(NSString *)jsonStr
{
    if (!jsonStr) {
        return nil;
    }
    NSError * error;
    return [NSJSONSerialization JSONObjectWithData:[jsonStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableLeaves error:&error];
}


/**
 解密

 @param response 網(wǎng)絡(luò)數(shù)據(jù)
 @return 解密結(jié)果
 */
+ (NSString *)decreptResponse:(NSString *)response{
    return response;
}

+ (id)modelTransformationWithResponseObj:(id)responseObj modelClass:(Class)modelClass{
    NSString *response = [[NSString alloc] initWithData:(NSData *)responseObj encoding:NSUTF8StringEncoding];
    response = [self decreptResponse:response];
    
    id tmp = [self convertJson:response];
    if ([tmp isKindOfClass:[NSArray class]]) {
        return [modelClass mj_objectArrayWithKeyValuesArray:responseObj];
    }else if([tmp isKindOfClass:[NSDictionary class]]){
        return [modelClass mj_objectWithKeyValues:responseObj];
    }
    return nil;
}

@end

6.Swift版本

import Foundation



/// 請(qǐng)求工具類
class NetworkService{
    
    
    /// Get 請(qǐng)求
    ///
    /// - Parameters:
    ///   - url: 請(qǐng)求地址
    ///   - params: 請(qǐng)求參數(shù)
    ///   - complete: 請(qǐng)求回調(diào) -- 回傳結(jié)果為對(duì)象
    static func GetRequest<T:NSObject>(url:String,
                           params:[String:AnyObject]?,
                           complete:@escaping (_ dataModel:T?,_ error:Error?)->()){
        
        RequsetManager.getRequest(url: url, params: params, success: { (result) in
            self.transformModel(result: result, complete: { (resultObj:T?,resultArr:[T]?) in
                complete(resultObj,nil)
            })
        }) { (error) in
            complete(nil,error)
        }
    }
    
    
    
    /// Get請(qǐng)求
    ///
    /// - Parameters:
    ///   - url: 請(qǐng)求地址
    ///   - params: 請(qǐng)求參數(shù)
    ///   - complete: 請(qǐng)求完成回調(diào) -- 回傳結(jié)果為對(duì)象數(shù)組
    static func GetRequest<T:NSObject>(url:String,
                           params:[String:AnyObject]?,
                           complete:@escaping (_ dataModelArray:[T]?,_ error:Error?)->()){
        
        RequsetManager.getRequest(url: url, params: params, success: { (result) in
            self.transformModel(result: result, complete: { (resultObj:T?,resultArr:[T]?) in
                complete(resultArr,nil)
            })
        }) { (error) in
            complete(nil,error)
        }
    }
    
    
    
    
    /// Post 請(qǐng)求
    ///
    /// - Parameters:
    ///   - url: 請(qǐng)求地址
    ///   - params: 請(qǐng)求參數(shù)
    ///   - complete: 請(qǐng)求回調(diào) -- 回傳結(jié)果為對(duì)象
    static func PostRequest<T:NSObject>(url:String,
                           params:[String:AnyObject]?,
                           complete:@escaping (_ dataModel:T?,_ error:Error?)->()){
        
        RequsetManager.postRequest(url: url, params: params, success: { (result) in
            self.transformModel(result: result, complete: { (resultObj:T?,resultArr:[T]?) in
                complete(resultObj,nil)
            })
        }) { (error) in
            complete(nil,error)
        }
    }
    
    
    
    /// Post請(qǐng)求
    ///
    /// - Parameters:
    ///   - url: 請(qǐng)求地址
    ///   - params: 請(qǐng)求參數(shù)
    ///   - complete: 請(qǐng)求完成回調(diào) -- 回傳結(jié)果為對(duì)象數(shù)組
    static func PostRequest<T:NSObject>(url:String,
                           params:[String:AnyObject]?,
                           complete:@escaping (_ dataModelArray:[T]?,_ error:Error?)->()){
        
        RequsetManager.postRequest(url: url, params: params, success: { (result) in
            self.transformModel(result: result, complete: { (resultObj:T?,resultArr:[T]?) in
                complete(resultArr,nil)
            })
        }) { (error) in
            complete(nil,error)
        }
    }
    
    
    
    
    
    
    
    /// 轉(zhuǎn)換方法
    ///
    /// - Parameters:
    ///   - result: 網(wǎng)絡(luò)返回?cái)?shù)據(jù)
    ///   - complete: 轉(zhuǎn)換完成回調(diào)
    static func transformModel<T:NSObject>(result:Any?,
                               complete:@escaping (_ result:T?,_ resultArray:[T]?)->()){
        
        
        let jsonStr = self.converNetData(result: result as Any)
        let obj = T.mj_object(withKeyValues: jsonStr)
        var objArr = [T]()
        if T.mj_objectArray(withKeyValuesArray: jsonStr) != nil {
            for item in T.mj_objectArray(withKeyValuesArray: jsonStr) {
                objArr.append(item as! T)
            }
        }
        complete(obj,objArr)
    }
    

    
    
    /// 轉(zhuǎn)換網(wǎng)絡(luò)數(shù)據(jù)
    ///
    /// - Parameter result: 網(wǎng)絡(luò)數(shù)據(jù)
    /// - Returns: 返回結(jié)果
    static func converNetData(result:Any) -> String{
        let respose = String.init(data: result as! Data, encoding: String.Encoding.utf8)
        return self.decreptRespose(respose: respose)

    }
    
    
    /// 解密
    ///
    /// - Parameter respose: 網(wǎng)絡(luò)數(shù)據(jù)
    /// - Returns: 解密結(jié)果
    static func decreptRespose(respose:String?) -> String{
        
        //自己實(shí)現(xiàn)解密
        return respose ?? "{}"
    }
}




/// 請(qǐng)求管理
class RequsetManager :AFHTTPSessionManager {
    
    
    /// 初始化RequsetManager單利
    static let sharedRequestManager:RequsetManager = {
        let instance = RequsetManager()
        instance.requestSerializer.timeoutInterval = 60.0
        //自定義設(shè)置請(qǐng)求頭
//        instance.requestSerializer.setValue(<#T##value: String?##String?#>, forHTTPHeaderField: <#T##String#>)
        instance.responseSerializer = AFHTTPResponseSerializer()
        return instance
    }()

    
    
    /// GET請(qǐng)求
    ///
    /// - Parameters:
    ///   - url: 請(qǐng)求地址
    ///   - params: 請(qǐng)求參數(shù)
    ///   - success: 請(qǐng)求成功回調(diào)
    ///   - failure: 請(qǐng)求失敗回調(diào)
    static func getRequest(url:String,
                    params:[String:AnyObject]?,
                    success:@escaping (_ responseData:Any?)->()?,
                    failure:@escaping (_ error:Error)->()?){
        
        RequsetManager.sharedRequestManager.get(url, parameters: params, progress: nil, success: {
            (task:URLSessionDataTask,result:Any?) in
            success(result)
            
        }) { (task:URLSessionDataTask?, error:Error) in
            failure(error)
        }
    }
    
    
    
    /// POST請(qǐng)求
    ///
    /// - Parameters:
    ///   - url: 請(qǐng)求地址
    ///   - params: 請(qǐng)求參數(shù)
    ///   - success: 請(qǐng)求成功回調(diào)
    ///   - failure: 請(qǐng)求失敗回調(diào)
    static func postRequest(url:String,
                    params:[String:AnyObject]?,
                    success:@escaping (_ responseData:Any?)->()?,
                    failure:@escaping (_ error:Error)->()?){
        RequsetManager.sharedRequestManager.post(url, parameters: params, progress: nil, success: {
            (task:URLSessionDataTask,result:Any?) in
            success(result)
            
        }) { (task:URLSessionDataTask?, error:Error) in
            failure(error)
        }
    }
}

7.對(duì)比--使用--分析

Swift版本其實(shí)就是對(duì)OC版本的一個(gè)更改在RequsetManager層次幾乎是一樣的,在Service層,OC版本需要在使用的時(shí)候?qū)⑺D(zhuǎn)換的對(duì)象的Class當(dāng)做參數(shù)傳過來,而在Swift版本中,僅僅需要在閉包中用泛型就可以,無需再需要傳遞對(duì)象的類
在使用上,OC版本回調(diào)回去的是一個(gè)id對(duì)象,無論是對(duì)象數(shù)組抑或是對(duì)象,都需要強(qiáng)制轉(zhuǎn)換后使用,而在Swift版本中,我用了多態(tài)的性質(zhì),根據(jù)使用時(shí)傳遞的閉包不同,返回的結(jié)果也不同,結(jié)果是對(duì)象的和結(jié)果是對(duì)象數(shù)組的,可以分別選擇調(diào)用相對(duì)應(yīng)的方法.

使用
返回結(jié)果是對(duì)象的OC版本:

- (void)test1{
    [RequestService AFN_JSONResponseGetWithUrl:@"http://192.168.1.107:8080/SkeeterTask/test/login" param:nil modelClass:[TestModel class] responseBlock:^(id dataObj, NSError *error) {
        if (error) {
            return ;
        }
        //使用上需要將id類型的dataObj進(jìn)行強(qiáng)轉(zhuǎn)...
        NSLog(@"結(jié)果為對(duì)象---%@",((TestModel *)dataObj).message);
    }];
}

返回結(jié)果是對(duì)象數(shù)組的OC版本:

[RequestService AFN_JSONResponseGetWithUrl:@"http://192.168.1.107:8080/SkeeterTask/test/login2" param:nil modelClass:[TestModel class] responseBlock:^(id dataObj, NSError *error) {
        if (error) {
            return ;
        }
        
        //此時(shí)沒必要強(qiáng)制轉(zhuǎn)換,如果網(wǎng)絡(luò)數(shù)據(jù)返回的是數(shù)組,得到的dataObj肯定是數(shù)組
        而且里面的對(duì)象肯定是傳的modelClass對(duì)象
        for (TestModel * mode in dataObj) {
            NSLog(@"結(jié)果為對(duì)象數(shù)組--%@",mode.message);
        }
    }];

返回結(jié)果是對(duì)象的Swift版本

NetworkService .GetRequest(url: "http://192.168.1.107:8080/SkeeterTask/test/login", params: nil) { (model:TestModel?, error:Error?) in
            if error != nil {
                print("Error")
                return
            }
            
            //調(diào)用方法的時(shí)候TestModel是當(dāng)做泛型傳遞過去的,所以回調(diào)的model
            是可以直接來使用的
            print("結(jié)果為對(duì)象--->\(model?.message ?? "NoMessage")")
        }

返回結(jié)果是對(duì)象數(shù)組的Swift版本

NetworkService.GetRequest(url: "http://192.168.1.107:8080/SkeeterTask/test/login2", params: nil) { (modelArr:[TestModel]?, error:Error?) in
            if error != nil{
                print("Error")
                return
            }
            
            //運(yùn)用傳遞的閉包不同,返回的modelArr的泛型為TestModel
            所以返回過來之后可以直接使用了
            if let arr = modelArr{
                for item in arr{
                    print("結(jié)果為對(duì)象數(shù)組--->\(item.message ?? "NoMessage")")
                }
            }
        }

分析,這兩中其實(shí)思想是一樣的,只不過Swift版本因?yàn)槭褂昧朔盒?所以在使用的時(shí)候更加清晰,而且也不需要將所要轉(zhuǎn)換的對(duì)象類當(dāng)做參數(shù)進(jìn)行傳遞,而且使用了多態(tài),根據(jù)返回的結(jié)果不同分別傳遞不同的閉包,而OC版本,始終都是那一個(gè)方法,需要對(duì)返回的結(jié)果進(jìn)行強(qiáng)制轉(zhuǎn)換,所以使用起來沒有那么的一目了然,大致就是這樣,希望我這個(gè)可以拋磚引玉,有好的想法的童鞋可以給我留言交流,共同提高

8.寫在最后

最后提醒一下,我請(qǐng)求的地址是我使用Java寫的本地的一個(gè)服務(wù),小伙伴們?cè)谑褂玫臅r(shí)候可要進(jìn)行相對(duì)應(yīng)的更改哦,最后奉上我測(cè)試的結(jié)果截圖,有什么不足呢,可以給我留言溝通共同提高哦.謝謝

Snip20170610_1.png
Snip20170610_2.png

10.最后的最后,奉上完整的Demo地址(XML轉(zhuǎn)JSON的類在OC源碼中)

OC版本源碼

Swift版本源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容