AFNetworking3.0的封装
AFNetworking在iOS网络请求第三方库中占据着半壁江山,前段时间将AFNetworking进行了3.0版本的迁移,运用面向对象的设计将代码进行封装整合,这篇文章主要为还在寻找AFNetworking集成代码或者准备3.0迁移的各位童鞋们提供思路,同时自定义了字典模型转换方法,需要的朋友也可以作为参考,还望各位老司机批评指正!先上代码框架图:
1、DB数据访问层,在AFNetworkingManager中我将AFNetworking的GET/POST/DELETE/PUT方法封装,提供了以下接口:
针对AFNetworking底层封装AFNetworkingManager后,是不是就可以直接在Service调用GET/POST/DELETE/PUT接口访问数据了呢?理论上是完全可以的,但是我们在实际开发中往往还需要自定义或者个性化一些效果如菊花等待框、阴影效果,提示文案等,所以本人建议在AFNetworkingManager基础上再包装一层专门用于Service对接,这样的好处是Service层完全不必关心AFNetworking的封装实现和序列化、授权等等问题,这样也便于后续的维护与版本的升级,好了我们再看看对接Service的ZTHttpManager:
好了,在这里完成了DB层的代码,访问API就毫无压力了!
2、模型基类JsonCommonResultBase/SerializationBaseModel
在这里我要说明一点,这里的模型基类是按照我们公司后台返回的API格式自定义,不一定适合每个人,但是可以作为各位的参考,具体的API返回数据结构为:
针对上述的数据格式,自定义的模型基类如下(BTW这里多层级的数据转化也是毫无压力的,完全OK):
NSDictionary *resultDic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableLeaves error:nil];
使用JSONObjectWithData后我们得到了NSDictionary格式的数据,但是我们需要的上面自定义对象模型数据啊!所以,你还需要一个序列化的工具在字典和模型间自如的转化,它就是SerializationTools!
值得注意的是这段代码,利用runtime获取模型的字段属性(代码段落,全部代码请移步SerializationTools实现类.m)
// 获取类成员变量和属性列表,ivarsCnt为类成员数量 unsigned int ivarsCnt = 0; 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:0]=='_'){ key=[key substringFromIndex:1]; } 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:0 length:10 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的网络数据访问框架就完成了!