AFNetworking在iOS网络请求第三方库中占据着半壁江山,前段时间将AFNetworking进行了3.0版本的迁移,运用面向对象的设计将代码进行封装整合,这篇文章主要为还在寻找AFNetworking集成代码或者准备3.0迁移的各位童鞋们提供思路,同时自定义了字典模型转换方法,需要的朋友也可以作为参考,还望各位老司机批评指正!先上代码框架图:

1、DB数据访问层,在AFNetworkingManager中我将AFNetworking的GET/POST/DELETE/PUT方法封装,提供了以下接口:

 /**
* get方式请求数据
*
* @param strUrl api地址
* @param headers 头部信息
* @param params 可变参数信息
* @param class 返回数据模型类
* @param block block结果回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
-(void)getDataFromUrl:(NSString *)strUrl
headers:(NSDictionary *)headers
params:(NSDictionary *)params
class:(Class)class
block:(CompletionLoad)block
blockError:(void (^)(JsonCommonResultBase *))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut; /**
* post方式更新数据
*
* @param strUrl api地址
* @param headers 头部信息
* @param params 可变参数信息
* @param class 返回数据模型类
* @param block block结果回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
- (void)postDataFromUrl:(NSString*)strUrl
headers:(NSDictionary*)headers
params:(NSDictionary*)params
class:(Class)class
block:(CompletionLoad)block
blockError:(void(^)(JsonCommonResultBase *))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut; /**
* put方式更新数据
*
* @param strUrl api地址
* @param headers 头部信息
* @param params 可变参数信息
* @param class 返回数据模型类
* @param block block结果回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
- (void)putDataFromUrl:(NSString*)strUrl
headers:(NSDictionary*)headers
params:(NSDictionary*)params
class:(Class)class
block:(CompletionLoad)block
blockError:(void(^)(id))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut; /**
* delete方式删除数据
*
* @param strUrl api地址
* @param headers 头部信息
* @param params 可变参数信息
* @param class 返回数据模型类
* @param block block结果回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
- (void)deleteDataFromUrl:(NSString*)strUrl
headers:(NSDictionary*)headers
params:(NSDictionary*)params
class:(Class)class
block:(CompletionLoad)block
blockError:(void(^)(JsonCommonResultBase *))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut; /**
* post方式更新数据(上传文件如图片)
*
* @param strUrl api地址
* @param headers 头部信息
* @param params 可变参数信息
* @param dataFiles 文件数据
* @param class 返回数据模型类
* @param block block结果回调
* @param progressBlock block进度回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
- (void)uploadDataFromUrl:(NSString *)strUrl
headers:(NSDictionary *)headers
params:(NSDictionary *)params
dataFiles:(NSArray *)dataFiles
progressBlock:(LoadProgress)progressBlock
block:(CompletionLoad)block
class:(Class)class
blockError:(void (^)(JsonCommonResultBase *))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut;

AFNetworking封装

针对AFNetworking底层封装AFNetworkingManager后,是不是就可以直接在Service调用GET/POST/DELETE/PUT接口访问数据了呢?理论上是完全可以的,但是我们在实际开发中往往还需要自定义或者个性化一些效果如菊花等待框、阴影效果,提示文案等,所以本人建议在AFNetworkingManager基础上再包装一层专门用于Service对接,这样的好处是Service层完全不必关心AFNetworking的封装实现和序列化、授权等等问题,这样也便于后续的维护与版本的升级,好了我们再看看对接Service的ZTHttpManager:

/**
* get方式请求数据(内部封装菊花等待框)
*
* @param strUrl api地址
* @param headers 头部信息
* @param params 可变参数信息
* @param parentView 菊花等待框寄托视图
* @param showShadow 是否阴影父视图
* @param blockRtn block结果回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
- (void)getDataToUrl:(NSString*)strUrl
headers:(NSDictionary*)headers
params:(NSDictionary*)params
parentView:(UIView*)parentView
showShadow:(BOOL)showShadow
class:(Class)class
blockRtn:(void (^)(id ))blockRtn
blockError:(void(^)(JsonCommonResultBase*))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut; /**
* post方式提交数据(内部封装菊花等待框)
*
* @param strUrl api地址
* @param headers 头部信息
* @param params 可变参数信息
* @param parentView 菊花等待框寄托父视图
* @param showShadow 是否阴影父视图
* @param blockRtn block结果回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
- (void)postDataToUrl:(NSString *)strUrl
headers:(NSDictionary *)headers
params:(NSDictionary *)params
parentView:(UIView *)parentView
showShadow:(BOOL)showShadow
class:(Class)class
blockRtn:(void (^)(id))blockRtn
blockError:(void(^)(JsonCommonResultBase*))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut; /**
* delete方式删除数据(内部封装菊花等待框)
*
* @param strUrl api地址
* @param headers 头部信息
* @param params 可变参数信息
* @param parentView 菊花等待框寄托视图
* @param showShadow 是否阴影父视图
* @param blockRtn block结果回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
- (void)deleteDataToUrl:(NSString*)strUrl
headers:(NSDictionary*)headers
params:(NSDictionary*)params
parentView:(UIView*)parentView
showShadow:(BOOL)showShadow
class:(Class)class
blockRtn:(void (^)(id ))blockRtn
blockError:(void(^)(JsonCommonResultBase*))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut; /**
* put方式提交数据(内部封装菊花等待框)
*
* @param strUrl api地址
* @param headers 头部信息
* @param params 可变参数信息
* @param parentView 菊花等待框寄托视图
* @param showShadow 是否阴影父视图
* @param blockRtn block结果回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
- (void)putDataToUrl:(NSString*)strUrl
headers:(NSDictionary*)headers
params:(NSDictionary*)params
parentView:(UIView*)parentView
showShadow:(BOOL)showShadow
class:(Class)class
blockRtn:(void (^)(id))blockRtn
blockError:(void(^)(JsonCommonResultBase*))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut; /**
* post方式上传文件
* @param strUrl api地址
* @param parentView 菊花等待框寄托视图
* @param showShadow 是否阴影父视图
* @param blockRtn block结果回调
* @param blockError block错误回调
* @param blockTimeOut block超时回调
*/
- (void)uploadImgFromUrl:(NSString*)strUrl
fileItems:(NSArray*)fileItems
parentView:(UIView*)parentView
showShadow:(BOOL)showShadow
headers:(NSDictionary *)headers
params:(NSDictionary *)params
class:(Class)class
blockProgress:(void (^)(NSString *))blockProgress
blockRtn:(void (^)(id))blockRtn
blockError:(void(^)(JsonCommonResultBase*))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut;

ZTHttpManager封装

好了,在这里完成了DB层的代码,访问API就毫无压力了!

2、模型基类JsonCommonResultBase/SerializationBaseModel

在这里我要说明一点,这里的模型基类是按照我们公司后台返回的API格式自定义,不一定适合每个人,但是可以作为各位的参考,具体的API返回数据结构为:

{
page = {
hasMore = ;
totalRows = ;
};
result = (
{
annexInfoStatus = Pending;
bsBeginTime = "2017-03-16 00:00:00";
bsEndTime = "2018-03-15 23:59:59";
bzBeginTime = "2017-03-16 00:00:00";
bzEndTime = "2018-03-15 23:59:59";
canRenewal = ;
company = {
code = alltrust;
id = ;
};
totalPremiums = "9632.09";
totalPremiumsText = "\U00a59,632.09";
verifyStatus = Verified;
}
);
status = ;
}

API返回200数据格式

针对上述的数据格式,自定义的模型基类如下(BTW这里多层级的数据转化也是毫无压力的,完全OK):

@interface SerializationBaseModel : NSObject<NSCopying>

// 获取列表字典
- (NSDictionary *)objectClassInArray; @end @implementation SerializationBaseModel - (id)copyWithZone:(NSZone *)zone{
return (id)self;
} // 获取列表字典 (具体result实现在子类中)
- (NSDictionary *)objectClassInArray{
return nil;
} @end

序列化model基类 需要继承NSCopying

#pragma mark - 分页数据模型
@interface ApiPage : SerializationBaseModel /**
* 总行数
*/
@property (nonatomic,assign) NSInteger totalRows; /**
* 是否还有数据
*/
@property (nonatomic,assign) BOOL hasMore; @end #pragma mark - Json数据模型
@interface JsonCommonResultBase : SerializationBaseModel /**
* 错误编码
*/
@property (nonatomic,copy) NSString *errCode; /**
* 错误消息
*/
@property (nonatomic,copy) NSString *errMsg; /**
* 请求状态
*/
@property (nonatomic,assign) NSInteger status; /**
* 分页信息
*/
@property (nonatomic,strong) ApiPage *page; @end //-------------------------线上是基类,线下是子类--------------------------------- #import "JsonCommonResultBase.h" @interface ZTTestModel : SerializationBaseModel @property (nonatomic,assign) NSInteger stuId; @property (nonatomic,copy) NSString *stuName; @property (nonatomic,copy) NSString *stuClassName; @property (nonatomic,copy) NSString *stuScore; @end @interface ZTTestModelResult : JsonCommonResultBase // BTW 实现多层级嵌套或者单数据模型也是没有问题的,可参照上面代码“api返回200数据格式”,这里定义好就行了 @property (nonatomic,strong) NSMutableArray<ZTTestModel*> *result; @end // 重点来了,对于列表格式的result使用NSMutableArray<ZTTestModel*> *result类似定义后就搞定了吗?那你就想太多了,我们还需要再实现代码中添加字典转化代码,如下:
#import "ZTTestModelResult.h" @implementation ZTTestModel @end @implementation ZTTestModelResult // 拿出小本本记好笔记,针对列表格式的result必须添加这段代码,单对象数据不需要
- (NSDictionary *)objectClassInArray{
return @{@"result" : [ZTTestModel class]};
} @end

返回数据基类与具体实现子类

 
那么重点来了,我们知道AFNetworking调用API后返回的数据格式流为:NSData -> NSDictionary ,我们需要先将responseObject数据从NSData转化为NSDictionary,这点在AFNetworkingManager中的已经写明:
NSDictionary *resultDic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableLeaves error:nil];

使用JSONObjectWithData后我们得到了NSDictionary格式的数据,但是我们需要的上面自定义对象模型数据啊!所以,你还需要一个序列化的工具在字典和模型间自如的转化,它就是SerializationTools!

// 转化为字典
- (NSMutableDictionary *)ToDictionary:(NSObject *)obj; // 获取属性数组
- (NSMutableDictionary *)ToKeyDictionary:(NSObject *)obj; // 字典填充对象
- (id)ToObjectOfDictionary:(NSDictionary *)dic class:(Class)class; // 转化为字典
- (NSData *)ToNSData:(NSObject *)obj; // 字典填充对象
- (id)ToObjectOfData:(NSData *)data class:(Class)class; /**
* 字典数组转换为对象数组
*
* @param class 对象类别名称
* @param array 数组
*
* @return 对象数组
*/
-(NSMutableArray *)GetObjectListOfArray:(Class)class array:(NSArray *)array; /**
* 对象数组转换为字典数组
*
* @param array 对象数组
*
* @return 字典数组
*/
-(NSArray *)GetDicListOfArray:(NSMutableArray *)array; /**
* json格式字符串转字典
*
* @param jsonString <#jsonString description#>
*
* @return <#return value description#>
*/
+ (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString; /**
* 字典转json字符串
*
* @param dic <#dic description#>
*
* @return <#return value description#>
*/
+ (NSString*)dictionaryToJson:(NSDictionary *)dic;

序列化工具类

值得注意的是这段代码,利用runtime获取模型的字段属性(代码段落,全部代码请移步SerializationTools实现类.m)

// 获取类成员变量和属性列表,ivarsCnt为类成员数量
unsigned int ivarsCnt = ; Ivar *ivars = class_copyIvarList(cls, &ivarsCnt); // 只获取类属性列表
// unsigned int outCount = 0;
// objc_property_t *properties =class_copyPropertyList(cls, &outCount); // 遍历成员变量列表,其中每个变量都是Ivar类型的结构体
for (const Ivar *p = ivars; p < ivars + ivarsCnt; ++p) { Ivar const ivar = *p; // 获取变量名
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 若此变量未在类结构体中声明而只声明为Property,则变量名加前缀 '_'下划线
// 比如 @property(retain) NSString *abc;则 key == _abc;
id value = [obj valueForKey:key]; if([key characterAtIndex:]=='_'){
key=[key substringFromIndex:];
}
if (value) {
[dictionaryFormat setObject:[value class] forKey:key];
} else {
// 获取类名
NSString *className = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; [dictionaryFormat setObject:[self GetClassByName:className] forKey:key];
}
}

通过序列化工具我们就可以很轻松的将字典转化为自定义模型了!

id resultJson = [[SerializationTools sharedInstance] ToObjectOfDictionary:self.resultDic class:class];

3、Service逻辑计算与服务提供

service层主要是根据实际业务需求来定义的接口,实现逻辑计算与业务组装,在这里我举一个例子如:

// 返回成功的结果、返回失败的信息、请求超时的错误都能通过block实现反向传值

.h文件

// GET
- (void)getStudentRecords:(NSInteger)offset
length:(NSInteger)length
parentView:(UIView *)parentView
blockRtn:(void (^)(ZTTestModelResult *))blockRtn
blockError:(void (^)(JsonCommonResultBase*))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut; -------------------------------分割线------------------------------------- .m文件 - (void)getStudentRecords:(NSInteger)offset
length:(NSInteger)length
parentView:(UIView *)parentView
blockRtn:(void (^)(ZTTestModelResult *))blockRtn
blockError:(void (^)(JsonCommonResultBase*))blockError
blockTimeOut:(TimeOutCompletion)blockTimeOut
{
NSString *strUrl = @"对接的api url"; [[ZTHttpManager sharedInstance]
getDataToUrl:strUrl
headers:nil
params:nil
parentView:parentView
showShadow:YES
class:[ZTTestModelResult class]
blockRtn:blockRtn
blockError:blockError
blockTimeOut:blockTimeOut];
}

4、Controller层业务诉求

// Get
[service getStudentRecords: length: parentView:self.view blockRtn:^(ZTTestModelResult *arryRtn) { // 回调成功,处理后续逻辑 } blockError:^(JsonCommonResultBase *error) { // show message about error } blockTimeOut:^{ // show message about timeout
}]; // Post
[service postTest:@"param1" param2:@"param2" param3:@"param3" parentView:self.view blockRtn:^(JsonCommonResultBase *result) { // 回调成功,处理后续逻辑 } blockError:^(JsonCommonResultBase *error) { // show message about error } blockTimeOut:^{ // show message about timeout
}]; // upload
[service uploadFileExpImage:[UIImage new] parentView:self.view blockProgress:^(NSString *progress) { // 上传进度回调成功,处理显示逻辑,注意刷新UI的操作一定要在主线程
dispatch_async(dispatch_get_main_queue(), ^{ //
}); } blockRtn:^(JsonCommonResultBase *rtn) { // 回调成功,处理后续逻辑 } blockError:^(JsonCommonResultBase *error) { // show message about error } blockTimeOut:^{ // show message about timeout }];

综上所述,一个基本的基于MVC的网络数据访问框架就完成了!

github地址:https://github.com/BeckWang0912/ZTAFNetworking.git

BTW:demo中主要是框架的搭建和AFNetworking的封装,不保证完全适用每个人的项目,我只提供设计思路,您来个性化,有不足之处还希望各位老司机多多包涵,如果对您又些许帮助的话,请在github上标个星星,您的鼓励是我写作的动力,不胜感激!

一劳永逸的解决AFNetworking3.0网络请求问题的更多相关文章

  1. 一劳永逸的解决AFNetworking3.0网络请求问题(面向对象封装大法,block回调)

    AFNetworking在iOS网络请求第三方库中占据着半壁江山,前段时间将AFNetworking进行了3.0版本的迁移,运用面向对象的设计将代码进行封装整合,这篇文章主要为还在寻找AFNetwor ...

  2. 基于AFNetworking3.0网络封装

    概述 对于开发人员来说,学习网络层知识是必备的,任何一款App的开发,都需要到网络请求接口.很多朋友都还在使用原生的NSURLConnection一行一行地写,代码到处是,这样维护起来更困难了. 对于 ...

  3. iOS_SN_基于AFNetworking3.0网络封装

    转发文章,原地址:http://www.henishuo.com/base-on-afnetworking3-0-wrapper/?utm_source=tuicool&utm_medium= ...

  4. android网络编程注意事项之一:移动网络下,防止网络超时甚至连接不上,解决办法--为网络请求设置代理

    Android应用程序访问互联网资源时,在Wifi的情况下处理网络连接按照上文所讲述的方法步骤即可顺利实现:但如果当前Android设备的联网方式是通过移动运营商的网络服务为中转,间接访问的互联网资源 ...

  5. [第三方]AFNetWorking3.0网络框架使用方法

    官网地址https://github.com/AFNetworking/AFNetworking #import <AFNetworking.h> - (void)viewDidLoad ...

  6. AFNetworking3.0 POST请求

    // 请求管理者 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.responseSerializer ...

  7. 基于AFNetWorking 3.0封装网络请求数据的类

    对于使用 AFNetworking 的朋友来说,很多朋友都是直接调用 AFNetworking的 API ,这样不太好,无法做到全工程统一配置. 最好的方式就是对网络层再封装一层,全工程不允许直接使用 ...

  8. 第六十二篇、AFN3.0封装网络请求框架,支持缓存

    1.网络请求 第一种实现方式: 功能:GET POST 请求 缓存逻辑: 1.是否要刷新本地缓存,不需要就直接发起无缓存的网络请求,否则直接读取本地数据 2.需要刷新本地缓存,先读取本地数据,有就返回 ...

  9. 一个App带你学会Retrofit2.0,麻麻再也不用担心我的网络请求了!

    Retrofit.Retrofit.Retrofit,越来越多的人在玩这个网络请求框架,这个由squareup公司开源的网络请求框架确实挺好用,今天我们就来看一下这个东东怎么玩! Retrofit作为 ...

随机推荐

  1. 基于Flask框架搭建视频网站的学习日志(六)之数据库

    使用Flask-SQLSlchemy管理数据库(1)--初步安装调试 一.介绍: Flask-SQLSlchemy是一个Flask扩展,简化了Flask中对sql的操作,是一个高层的框架,可以避免直接 ...

  2. nm命令介绍

    一.参考文章 网址1:https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/nm.html 参考2: man nm 参考3:<linux ...

  3. 给c盘瘦身

    火狐浏览器缓存 C:\Users\lenovo\AppData\Local\Mozilla\Firefox\Profiles\5nk022sw.default\cache2\entries ‪C:\U ...

  4. 16)PHP, set_include_path

    代码展示: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...

  5. [原]调试实战——使用windbg调试崩溃在ole32!CStdMarshal::DisconnectSrvIPIDs

    原调试debugwindbg崩溃crash 前言 最近程序会不定期崩溃,很是头疼!今晚终于忍无可忍,下决心要干掉它!从之前的几个相关的dump可以猜到是有接口未释放导致的问题,但没有确认到底是哪个接口 ...

  6. IDEA Maven项目中添加tomcat没有无artifact选项

    IntelliJ使用 ##使用IntelliJ IDEA配置web项目时,选择Edit Configration部署Tomcat的Deployment可能会出现以下情况: 导致新手部署过程中摸不着头脑 ...

  7. MySQL表查询

    单表查询 表准备 create table emp( id int not null unique auto_increment, name ) not null, sex enum('male',' ...

  8. 关于使用MyEclipse自动生成Hibernate和Struts出现的jar不兼容的问题(antlr.collections.AST.getLine()I)

    今天做Hibernate和Struts2结合的练习,使用MyEclipse自动创建Hibernate和Struts的相关配置文件和jar包,写完一个查询方法后,准备测试一下结果,简单的查询那种,很诡异 ...

  9. github简单操作

    配置用户名: git config --global user.name 名.姓 配置用户邮件:git config --global user.email 名.姓@avatarmind.com 查看 ...

  10. 分布式消息队列Apache Pulsar

    Pulsar简介 Apache Pulsar是一个企业级的分布式消息系统,最初由Yahoo开发并在2016年开源,目前正在Apache基金会下孵化.Plusar已经在Yahoo的生产环境使用了三年多, ...