一劳永逸的解决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官网退 ...
随机推荐
- React学习(一)父子组件通讯
React父子组件之间通讯,利用props和state完成,首先React是单向数据流,父组件可以向子组件传递props: 实现父子组件双向数据流整体的思路是: 1,父组件可以向子组件传递props, ...
- Java学习记录第一章
学习Java第一章的记录,这一章主要记录的是Java的最基础部分的了解知识,了解Java的特性和开发环境还有Java语言的优缺点. 计算机语言的发展大概过程:机器语言--->汇编语言---> ...
- JavaEE开发之SpringBoot工程的创建、运行与配置
本篇博客我们就来聊一下如何使用Eclipse+STS插件来创建Spring Boot的工程.Spring Boot可以使我们更容易的使用Spring框架,在Spring Boot中自动配置了好多东西, ...
- poj1182食物链,经典带权并查集
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人用两种 ...
- Quartz (一)
1 核心接口 1.1 Scheduler---核心调度器 1.2 Job---任务 1.3 JobDetail---任务描述 1.4 Trigger---触发器 2 触发器 Tigger(CornTr ...
- IOS(二)基本控件UIButton、简易动画、transform属性、UIImageView
UIButton //1.设置UIButton 的左右移动 .center属性 获得 CGPoint 来修改x y //1.设置UIButton 的放大缩小 bounds属性 获得CGRect 然后通 ...
- hdu1520 Anniversary party 简单树形DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1520 思路:树形DP的入门题 定义dp[root][1]表示以root为根节点的子树,且root本身参 ...
- 小谈-—ServletConfig对象和servletContext对象
一.servletContext概述 servletContext对象是Servlet三大域对象之一,每个Web应用程序都拥有一个ServletContext对象,该对象是Web应用程序的全局对象或者 ...
- 设计模式浅谈----策略模式(c#及java实现)
一.何为策略模式 策略模式是行为型模式的一种,主要用于需要使用不同的算法来处理不同的数据对象时使用,是一种可以在运行时选择算法的设计模式.也称为政策模式. 主要解决:在有多种算法相似的情况下,使用 i ...
- Vuejs实例-02Vue.js项目集成ElementUI
Vuejs实例-02Vue.js项目集成ElementUI 0:前言 vue.js的UI组件库,在git上有多个项目,我见的使用者比较多的是iView和Element.两个组件库,组件都很丰富. 官网 ...