zoukankan      html  css  js  c++  java
  • 一劳永逸的解决AFNetworking3.0网络请求问题

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

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

     1 /**
     2  *  get方式请求数据
     3  *
     4  *  @param strUrl          api地址
     5  *  @param headers         头部信息
     6  *  @param params          可变参数信息
     7  *  @param class           返回数据模型类
     8  *  @param block           block结果回调
     9  *  @param blockError      block错误回调
    10  *  @param blockTimeOut    block超时回调
    11  */
    12 -(void)getDataFromUrl:(NSString *)strUrl
    13               headers:(NSDictionary *)headers
    14                params:(NSDictionary *)params
    15                 class:(Class)class
    16             block:(CompletionLoad)block
    17             blockError:(void (^)(JsonCommonResultBase *))blockError
    18             blockTimeOut:(TimeOutCompletion)blockTimeOut;
    19 
    20 /**
    21  *  post方式更新数据
    22  *
    23  *  @param strUrl          api地址
    24  *  @param headers         头部信息
    25  *  @param params          可变参数信息
    26  *  @param class           返回数据模型类
    27  *  @param block           block结果回调
    28  *  @param blockError      block错误回调
    29  *  @param blockTimeOut    block超时回调
    30  */
    31 - (void)postDataFromUrl:(NSString*)strUrl
    32                 headers:(NSDictionary*)headers
    33                  params:(NSDictionary*)params
    34                   class:(Class)class
    35             block:(CompletionLoad)block
    36             blockError:(void(^)(JsonCommonResultBase *))blockError
    37             blockTimeOut:(TimeOutCompletion)blockTimeOut;
    38 
    39 /**
    40  *  put方式更新数据
    41  *
    42  *  @param strUrl          api地址
    43  *  @param headers         头部信息
    44  *  @param params          可变参数信息
    45  *  @param class           返回数据模型类
    46  *  @param block           block结果回调
    47  *  @param blockError      block错误回调
    48  *  @param blockTimeOut    block超时回调
    49  */
    50 - (void)putDataFromUrl:(NSString*)strUrl
    51                headers:(NSDictionary*)headers
    52                 params:(NSDictionary*)params
    53                  class:(Class)class
    54             block:(CompletionLoad)block
    55             blockError:(void(^)(id))blockError
    56             blockTimeOut:(TimeOutCompletion)blockTimeOut;
    57 
    58 /**
    59  *  delete方式删除数据
    60  *
    61  *  @param strUrl          api地址
    62  *  @param headers         头部信息
    63  *  @param params          可变参数信息
    64  *  @param class           返回数据模型类
    65  *  @param block           block结果回调
    66  *  @param blockError      block错误回调
    67  *  @param blockTimeOut    block超时回调
    68  */
    69 - (void)deleteDataFromUrl:(NSString*)strUrl
    70                   headers:(NSDictionary*)headers
    71                    params:(NSDictionary*)params
    72                     class:(Class)class
    73             block:(CompletionLoad)block
    74             blockError:(void(^)(JsonCommonResultBase *))blockError
    75             blockTimeOut:(TimeOutCompletion)blockTimeOut;
    76 
    77 /**
    78  *  post方式更新数据(上传文件如图片)
    79  *
    80  *  @param strUrl          api地址
    81  *  @param headers         头部信息
    82  *  @param params          可变参数信息
    83  *  @param dataFiles       文件数据
    84  *  @param class           返回数据模型类
    85  *  @param block           block结果回调
    86  *  @param progressBlock   block进度回调
    87  *  @param blockError      block错误回调
    88  *  @param blockTimeOut    block超时回调
    89  */
    90 - (void)uploadDataFromUrl:(NSString *)strUrl
    91                   headers:(NSDictionary *)headers
    92                    params:(NSDictionary *)params
    93                 dataFiles:(NSArray *)dataFiles
    94             progressBlock:(LoadProgress)progressBlock
    95                     block:(CompletionLoad)block
    96                     class:(Class)class
    97             blockError:(void (^)(JsonCommonResultBase *))blockError
    98             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 = 0;
            totalRows = 1;
        };
        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 = 0;
                company =             {
                    code = alltrust;
                    id = 16;
                };
                totalPremiums = "9632.09";
                totalPremiumsText = "U00a59,632.09";
                verifyStatus = Verified;
            }
        );
        status = 200;
    }
    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 = 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的网络数据访问框架就完成了!

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

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

  • 相关阅读:
    dtoi4641 小店购物
    arcgis开发团队(Tel:13261043797 QQ:1216807928)中科燕园ArcGIS开发团队
    gis开发团队承接gis项目手机13261043797,qq:1216807928
    arcgis开发团队(Tel:13261043797 QQ:1216807928 )中科燕园arcgis开发团队
    gis项目开发团队承接GIS兼职,祝大家2017年工作顺利 事事如意Tel:13261043797 QQ:1216807928
    中科燕园GIS外包团队 手机:13261043797 QQ:1216807928
    中科燕园GIS外包团队(手机:13261043797 QQ:1216807928)地理平台:“互联网+”政企转型的新入口
    中科燕园GIS开发团队 手机13261043797 QQ:1216807928
    中科燕园GIS外包团队(手机:13261043797 QQ:1216807928)地理智慧为旅游带来全新价值
    中科燕园GIS外包团队(手机:13261043797 QQ:1216807928)---ArcGIS 10.3.1正式发布
  • 原文地址:https://www.cnblogs.com/beckwang0912/p/7052782.html
Copyright © 2011-2022 走看看