一劳永逸的解决AFNetworking3.0网络请求问题
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网络请求问题的更多相关文章
- 一劳永逸的解决AFNetworking3.0网络请求问题(面向对象封装大法,block回调)
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 ...
- 基于AFNetWorking 3.0封装网络请求数据的类
对于使用 AFNetworking 的朋友来说,很多朋友都是直接调用 AFNetworking的 API ,这样不太好,无法做到全工程统一配置. 最好的方式就是对网络层再封装一层,全工程不允许直接使用 ...
- 第六十二篇、AFN3.0封装网络请求框架,支持缓存
1.网络请求 第一种实现方式: 功能:GET POST 请求 缓存逻辑: 1.是否要刷新本地缓存,不需要就直接发起无缓存的网络请求,否则直接读取本地数据 2.需要刷新本地缓存,先读取本地数据,有就返回 ...
- 一个App带你学会Retrofit2.0,麻麻再也不用担心我的网络请求了!
Retrofit.Retrofit.Retrofit,越来越多的人在玩这个网络请求框架,这个由squareup公司开源的网络请求框架确实挺好用,今天我们就来看一下这个东东怎么玩! Retrofit作为 ...
随机推荐
- js操作元素导致元素错位和大小改变
使用js循环的方式批量控制元素的大小时结果往往不尽如人意. 我总结了一条规律 在一个循环体内不可以同时存在一下两种操作,否则容易导致元素错位或大小改变: 1.对元素的offsetWidth.offse ...
- SQL触发器笔记
触发器(Trigger)是在对表进行插入.更新.删除等操作时自动执行的存储过程. 触发器是一种特殊的存储过程,它在执行语言事件时自动生效,采用事件驱动机制.当某个触发事件发生时,定义在触发器中的功能将 ...
- intellij debug模式提示 Method breakpoints may dramatically slow down debugging
之前不小心打了一个断点,然后项目长时间不能启动,保持一个加载的状态,并且提示Method breakpoints may dramatically slow down debugging,百度之后才知 ...
- 第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第十天】(单点登录系统实现)
https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040 ...
- 关于前端JS的总结
简介 JavaScript是一种计算机编程语言,可以像等其他编程语言那样定义变量,执行循环等.主要执行在浏览器上,为HTML页面提供动态效果,而且JavaScript是一种脚本语言,它的代码是解释执行 ...
- 上传excel文件,读取内容,增加事务写入数据库
package com.inspur.icpmg.itss.asset.dao.impl; import com.inspur.icpmg.util.DBHelper; import org.apac ...
- 编译原理_P1001
1 绝大部分软件使用高级程序设计语言来编写.用这些语言编写的软件必须经过编译器的编译,才能转换为可以在计算机上运行的机器代码.编译器所生成代码的正确性和质量会直接影响成千上万的软件.虽然大部分人不会参 ...
- 启动查看crontab日志服务
方法1: . 修改rsyslog文件,将/etc/rsyslog.d/-default.conf 文件中的#cron.*前的#删掉: . 重启rsyslog服务service rsyslog rest ...
- 50)PHP,单例模式
class B{ ; private static $instance; private function __construct(){} public static function getNew( ...
- D. Fish eating fruit
题:https://nanti.jisuanke.com/t/41403 题意:求任意俩点之间距离之和模3后的三个结果的总数(原距离之和) 第一种做法: 树形dp #include<bits/s ...