一劳永逸的解决AFNetworking3.0网络请求问题(面向对象封装大法,block回调)
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
返回数据基类与具体实现子类
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网络请求问题(面向对象封装大法,block回调)的更多相关文章
- 一劳永逸的解决AFNetworking3.0网络请求问题
AFNetworking在iOS网络请求第三方库中占据着半壁江山,前段时间将AFNetworking进行了3.0版本的迁移,运用面向对象的设计将代码进行封装整合,这篇文章主要为还在寻找AFNetwor ...
- 基于AFNetworking3.0网络封装
概述 对于开发人员来说,学习网络层知识是必备的,任何一款App的开发,都需要到网络请求接口.很多朋友都还在使用原生的NSURLConnection一行一行地写,代码到处是,这样维护起来更困难了. 对于 ...
- iOS_SN_基于AFNetworking3.0网络封装
转发文章,原地址:http://www.henishuo.com/base-on-afnetworking3-0-wrapper/?utm_source=tuicool&utm_medium= ...
- android网络编程注意事项之一:移动网络下,防止网络超时甚至连接不上,解决办法--为网络请求设置代理
Android应用程序访问互联网资源时,在Wifi的情况下处理网络连接按照上文所讲述的方法步骤即可顺利实现:但如果当前Android设备的联网方式是通过移动运营商的网络服务为中转,间接访问的互联网资源 ...
- [第三方]AFNetWorking3.0网络框架使用方法
官网地址https://github.com/AFNetworking/AFNetworking #import <AFNetworking.h> - (void)viewDidLoad ...
- AFNetworking3.0 POST请求
// 请求管理者 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.responseSerializer ...
- 网络请求之jsonp封装
首先介绍下jsonp原理 浏览器因为同源策略的限制,在不同源的服务器通过我们传统axios是不能直接用来请求数据的(忽略代理),而src标签则不受同源策略的影响,所以我们需要动态的创建带有src的标签 ...
- AFNetworking3.0+MBProgressHUD二次封装,一句话搞定网络提示
对AFNetworking3.0+MBProgressHUD的二次封装,使用更方便,适用性非常强: 一句话搞定网络提示: 再也不用担心网络库更新后,工程要修改很多地方了!网络库更新了只需要更新这个封装 ...
- Volley网络请求框架的基本用法
备注: 本笔记是参照了 http://blog.csdn.net/ysh06201418/article/details/46443235 学习之后写下的 简介: Volley是google官网退 ...
随机推荐
- H5前端框架推荐合集
Ionic ionic 吧开发流程都帮你做好了,已经不再是单纯的UI框架,而是开发框架了,非常适合快速开发.基于angular2,丰富的UI组件,大大改进的编程模型, Semantic UI 中文官网 ...
- 【原创】Android 5.0 BLE低功耗蓝牙从设备应用
如果各位觉得有用,转载+个出处. 现如今安卓的低功耗蓝牙应用十分普遍了,智能手环.手表遍地都是,基本都是利用BLE通信来交互数据.BLE基本在安卓.IOS两大终端设备上都有很好支持,所以有很好发展前景 ...
- JS面向对象,创建,继承
很开心,最近收获了很多知识,而且发现很多东西,以前理解的都是错的,或者是肤浅的,还以为自己真的就get到了精髓,也很抱歉会影响一些人往错误的道路上走,不过这也告诉了我们,看任何一篇文章都不能盲目的去相 ...
- PHP学习笔记-4(时间戳)
在学习PHP时间戳的时候,发现了一个有趣的现象,就是发现用strtotime()这个函数返回的时间戳跟人家的不一样,以为是自己哪里写错了,后来发现不是这样的. 是因为设置的时区不同,从而导致了时间显示 ...
- hdu4185二分图匹配
Thanks to a certain "green" resources company, there is a new profitable industry of oil s ...
- Hibernate配置文件中配置各种数据库链接
hibernate.properties ###################### ### Query Language ### ###################### ## define ...
- Bootstrap之折叠(Collapse)插件
学习资料:Bootstrap折叠(Collapse)插件 大家可能常见的都是类似: 这种的效果,小颖今天要给大家分享一个不一样的效果嘻嘻.铛铛铛铛........................... ...
- Linux实战教学笔记11:linux定时任务
第十一节 linux定时任务 标签(空格分隔): Linux实战教学笔记 ---更多资料点我查看 1.1 定时任务Crond介绍 Crond是linux系统中用来定期执行命令/脚本或指定程序任务的一种 ...
- OC中自定义构造方法
格式 -(instancetype)init(){ self=[super init] if(self){ } return self; } 自定义构造方法规范 1)一定是对象方法,以减号开头 2)返 ...
- Oracle正则表达式之匹配网址
利于正则表达式匹配出网址 --1 表准备create table test_regexp( object varchar2(50)); --2 数据准备 insert into test_regexp ...