解决AFNetworking3.0网络请求问题

释放双眼,带上耳机,听听看~!

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

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

 AFNetworking封装

针对AFNetworking底层封装AFNetworkingManager后,是不是就可以直接在Service调用GET/POST/DELETE/PUT接口访问数据了呢?理论上是完全可以的,但是我们在实际开发中往往还需要自定义或者个性化一些效果如菊花等待框、阴影效果,提示文案等,所以本人建议在AFNetworkingManager基础上再包装一层专门用于Service对接,这样的好处是Service层完全不必关心AFNetworking的封装实现和序列化、授权等等问题,这样也便于后续的维护与版本的升级,好了我们再看看对接Service的ZTHttpManager:

 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;
}

针对上述的数据格式,自定义的模型基类如下(BTW这里多层级的数据转化也是毫无压力的,完全OK):

复制代码
@interface SerializationBaseModel : NSObject<NSCopying>

// 获取列表字典
- (NSDictionary *)objectClassInArray;

@end


@implementation SerializationBaseModel

- (id)copyWithZone:(NSZone *)zone{
    return (id)self;
}

// 获取列表字典 (具体result实现在子类中)
- (NSDictionary *)objectClassInArray{
    return nil;
}

@end
复制代码

复制代码
#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

人已赞赏
iOS文章

iOS适配UIViewView/WKWebView,H5生成长图,仿微信进度条

2019-10-26 13:33:48

iOS文章

iOS开发正则表达式

2019-10-26 15:20:54

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索