Swift3.0 + AFN3.0 + MJExtention + (泛型)封装网络请求类

写在前面:对于移动端开发来说,无论iOS,Android,抑或是H5开发,所谓的开发核心之一就是从服务端接收数据,前端负责展示,所以,当然还有各种逻辑的实现,但是呢,一个完善的APP肯定是少不了与服务端交互的,所以一个网络请求类的封装好用与否直接关系到一个项目的开发效率以及后期的维护,本人也接手过那种没有请求类的项目呢,每个请求写了一堆代码,用的全部是ASI,,然后需要更改为AFN的时候,哇,简直无敌了,然后Command+F都用不上,只能一处处找,找的自己都不知道时间了,真的相当折磨,受过这个折磨的童鞋肯定能够体会这种痛苦,有种删除项目,拍屁股走人的邪恶想法,但是呢,毕竟咱的职业素养还在,所以就耐心的享受这折磨了.废话不多说,相信每个开发的童鞋都能知道一个好的网络请求类带给我们的便捷之处.

** 1.Swift3.0**

不用过多介绍什么,Swift是苹果主推的开发语言,老司机都知道原来是OC,Swift语言笔者前天花了一下午+一晚上的时间学习了下,确实很简洁,开发速度确实很快,风格类似于(JS+Python)我比较喜欢的是Swift中的泛型,泛型这个东西Java中有,当初写Java的时候泛型真的让我方便了很多很多,所以在进行iOS开发的时候一直琢磨着有没有泛型这个东西,可是呢,都知道没有...最近安卓新出的kotlin语言,跟Swift也是大同小异吧.

** 2.AFN**

AFN是一套iOS网络请求框架,用OC语言编写(我记得原来有Swift语言编写的,但是去GitHub上找的时候没有找到)这个框架现在几乎所有的iOS项目都在用吧,确实很好用,当然不缺乏大牛公司自己封装的,当然也可能有老项目还在用ASI(曾经火了好长时间,但是现在没人用了,WHy?因为作者不更新了...可悲不)

** 3.MJExtention**

MJExtention是李明杰大神的一套框架,主要用来字典转模型,这个框架的好用程度本人给100分,当然还有别的功能,其他的童鞋们慢慢去挖掘吧.即便你的对象里包含对象数组,它也可以帮你全部转换的哦,有不懂的或者有兴趣的可以直接去GitHub上下载学习一下,或者给我留言也可以哦.

4.封装思想

我也相信这些封装思想应该很多人都知道了,思想就像风暴一样,瞬间可以席卷全球,真是个神奇的东西,当然我的这个也是来源于网络上大神的分享.
AFN的源码我看了一遍,我们可以直接用AFHTTPSessionManager,有兴趣的童鞋可以研究下AFN的源码,然后我的想法是继承AFHTTPSessionManager创建一个RequsetManager类,(当然第一版我用的是装饰模式,是在RequsetManager中装饰一个AFHTTPSessionManager对象,当然两种方法各有各的优点,直接继承可能调用方法上更加简洁一点,然后也会减少一个对象的使用...)提供一个RequsetManager单例,然后创建一个NetworkService类,为所有的请求提供服务,在这个里面使用包装RequsetManager,使用RequsetManager下载数据,在NetworkService中提供对数据的解密,缓存(当然我没有去实现缓存的功能),以及对数据的解析转换(MJExtention在这儿真的起了很大的作用--在此转换数据的时候我们需要知道将json数据转换为什么样的对象,在OC版本中实现的时候我们是将要转换对象的Class当做参数传了过去,但是返回的结果是id的,我们用的时候需要进行强转,但是在Swift中我们有了泛型,所以这一步就简洁了很多,具体的祥看代码,后面会同样奉上OC版本的封装).

5.OC版本

RequsetManager:仅仅实现了Post和GET方法,声明了成功和失败的Block回调,一个重新载入请求头的,当然大部分是没有用的,另外一个是获取单利的,但是,所有的方法均是静态方法,获取单利是可以在外部单独设置请求头


#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

实现如下:

#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:仅仅封装了返回结果为JSON的,其实有XMLDictionary可以将XML转换为JSON的,有兴趣的可以从后面我的GitHub上下载demo ,里面包含了这个类,当然也可以直接从GitHub上搜索

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

/**
 重新载入请求头
 */
+ (void)reloadAFNHeaderAuth;

/**
 利用AFN请求获取JSON返回 --- GET请求

 @param url 请求地址
 @param param 请求参数
 @param modelClass 请求返回所需要转换的模型类
 @param responseBlock 请求成功|失败回调
 */
+ (void)AFN_JSONResponseGetWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock) responseBlock;

/**
 利用AFN请求获取JSON返回 --- POST请求
 
 @param url 请求地址
 @param param 请求参数
 @param modelClass 请求返回所需要转换的模型类
 @param responseBlock 请求成功|失败回调
 */
+ (void)AFN_JSONResponsePostWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock)responseBlock;


/**
 请求返回结果转换为模型方法  -- 此方法不需要关注,如果有Service继承此类,可以重写该方法进行数据处理

 @param responseObj 返回结果
 @param modelClass 模型类
 @return 转换成功的模型
 */
+ (id)modelTransformationWithResponseObj:(id)responseObj modelClass:(Class)modelClass;

@end

具体实现如下:

#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 网络数据
 @return 解密结果
 */
+ (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



/// 请求工具类
class NetworkService{
    
    
    /// Get 请求
    ///
    /// - Parameters:
    ///   - url: 请求地址
    ///   - params: 请求参数
    ///   - complete: 请求回调 -- 回传结果为对象
    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请求
    ///
    /// - Parameters:
    ///   - url: 请求地址
    ///   - params: 请求参数
    ///   - complete: 请求完成回调 -- 回传结果为对象数组
    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 请求
    ///
    /// - Parameters:
    ///   - url: 请求地址
    ///   - params: 请求参数
    ///   - complete: 请求回调 -- 回传结果为对象
    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请求
    ///
    /// - Parameters:
    ///   - url: 请求地址
    ///   - params: 请求参数
    ///   - complete: 请求完成回调 -- 回传结果为对象数组
    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)
        }
    }
    
    
    
    
    
    
    
    /// 转换方法
    ///
    /// - Parameters:
    ///   - result: 网络返回数据
    ///   - complete: 转换完成回调
    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)
    }
    

    
    
    /// 转换网络数据
    ///
    /// - Parameter result: 网络数据
    /// - Returns: 返回结果
    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: 网络数据
    /// - Returns: 解密结果
    static func decreptRespose(respose:String?) -> String{
        
        //自己实现解密
        return respose ?? "{}"
    }
}




/// 请求管理
class RequsetManager :AFHTTPSessionManager {
    
    
    /// 初始化RequsetManager单利
    static let sharedRequestManager:RequsetManager = {
        let instance = RequsetManager()
        instance.requestSerializer.timeoutInterval = 60.0
        //自定义设置请求头
//        instance.requestSerializer.setValue(<#T##value: String?##String?#>, forHTTPHeaderField: <#T##String#>)
        instance.responseSerializer = AFHTTPResponseSerializer()
        return instance
    }()

    
    
    /// GET请求
    ///
    /// - Parameters:
    ///   - url: 请求地址
    ///   - params: 请求参数
    ///   - success: 请求成功回调
    ///   - failure: 请求失败回调
    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请求
    ///
    /// - Parameters:
    ///   - url: 请求地址
    ///   - params: 请求参数
    ///   - success: 请求成功回调
    ///   - failure: 请求失败回调
    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.对比--使用--分析

Swift版本其实就是对OC版本的一个更改在RequsetManager层次几乎是一样的,在Service层,OC版本需要在使用的时候将所要转换的对象的Class当做参数传过来,而在Swift版本中,仅仅需要在闭包中用泛型就可以,无需再需要传递对象的类
在使用上,OC版本回调回去的是一个id对象,无论是对象数组抑或是对象,都需要强制转换后使用,而在Swift版本中,我用了多态的性质,根据使用时传递的闭包不同,返回的结果也不同,结果是对象的和结果是对象数组的,可以分别选择调用相对应的方法.

使用
返回结果是对象的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进行强转...
        NSLog(@"结果为对象---%@",((TestModel *)dataObj).message);
    }];
}

返回结果是对象数组的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 ;
        }
        
        //此时没必要强制转换,如果网络数据返回的是数组,得到的dataObj肯定是数组
        而且里面的对象肯定是传的modelClass对象
        for (TestModel * mode in dataObj) {
            NSLog(@"结果为对象数组--%@",mode.message);
        }
    }];

返回结果是对象的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
            }
            
            //调用方法的时候TestModel是当做泛型传递过去的,所以回调的model
            是可以直接来使用的
            print("结果为对象--->\(model?.message ?? "NoMessage")")
        }

返回结果是对象数组的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
            }
            
            //运用传递的闭包不同,返回的modelArr的泛型为TestModel
            所以返回过来之后可以直接使用了
            if let arr = modelArr{
                for item in arr{
                    print("结果为对象数组--->\(item.message ?? "NoMessage")")
                }
            }
        }

分析,这两中其实思想是一样的,只不过Swift版本因为使用了泛型,所以在使用的时候更加清晰,而且也不需要将所要转换的对象类当做参数进行传递,而且使用了多态,根据返回的结果不同分别传递不同的闭包,而OC版本,始终都是那一个方法,需要对返回的结果进行强制转换,所以使用起来没有那么的一目了然,大致就是这样,希望我这个可以抛砖引玉,有好的想法的童鞋可以给我留言交流,共同提高

8.写在最后

最后提醒一下,我请求的地址是我使用Java写的本地的一个服务,小伙伴们在使用的时候可要进行相对应的更改哦,最后奉上我测试的结果截图,有什么不足呢,可以给我留言沟通共同提高哦.谢谢

Snip20170610_1.png
Snip20170610_2.png

10.最后的最后,奉上完整的Demo地址(XML转JSON的类在OC源码中)

OC版本源码

Swift版本源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 一直说细节决定成败,因我本身也是马大哈的那种,对提到的细节类一向本能抵触也不放在心上。在工作上反映出来,不是交到人...
    龙少之说阅读 469评论 5 11
  • 1.StarCitizen星际公民3.0菜单、键位图文汉化 https://www.jianshu.com/p/a...
    星际公民阅读 3,372评论 0 2
  • 我一直认为,写东西就是把心中所思所想用文字这个媒介表达出来,所以我一直都是遇到什么事或者突然产生什么想法就写下来...
    夜吟月光阅读 1,419评论 4 51