美文网首页iOS干货
菜鸟用RAC基于AFNetworking 3.0网络的封装搭建记

菜鸟用RAC基于AFNetworking 3.0网络的封装搭建记

作者: timeQuick | 来源:发表于2019-03-31 21:58 被阅读287次

    前言

    这几天自己在学习RAC,学习了几天对RAC有了一定基础了解,如之前写的
    菜鸟开始学习ReactiveObjc运用的记录(二)
    菜鸟运用RAC对replay,replayLast,replayLazily记录(三)
    看着前面写的,虽然是基础,但自己坚持记录下来,心里还是有满满的成就感的,哈哈~
    好,回到我今天学到的东西。在学习RAC的几天,心里就想着怎么把RAC用到自己的工程中,因为实践后才是自己的东西! gitHub代码地址:https://github.com/CgtST/ReactiveObjCDemo.git

    怎么开始封装呢

    自己以前的工程是用的AFNetWorking网络库,一般是写个单例对AFNetWorking的二次封装。但如果用万物皆信号的RAC又怎么处理,其实在心里琢磨了很久。网上有用RAC中的 command来表示请求来执行的过程的,有用的以前的网络类中直接改成Singnal方式的。 这里我用RACSignal replayzily 来处理。于是用xcode新建工程,边写边思考来写。
    心里琢磨着建立几个类呢?建立了一个请求之前的处理类,请求执行类,数据返回处理类,后面还有个写base url等宏的文件

    屏幕快照 2019-03-29 下午6.31.19.png

    这几个文件的打算功能

    • PAHttpRequest: 处理网络请求参数类型,如每个请求有固定的参数(app 版本号,客户端ios等),处理base url与path路径的拼接,以及请求参数的加密等内容,解析成指定类的参数;
    • PAHTTPService:对AF的二次封装处理Get,Post,文件上传等功能了。
    • PAHTTPResponse:解析成指定的数据模型,返回数据的处理,错误处理等

    请求参数类PAHttpRequest

    在PAHttpRequest.h中如下,自己只定义了一个方法想到时返回指定的数据model类。

    typedef NS_ENUM(NSUInteger , PARequestType)
    {
        PARequestTypePost = 0, //Post请求
        PAHttpRequestGet,
    };
    
    @interface PAHttpRequest : NSObject
    
    @property (nonatomic,copy) NSString * path; //就基于baseUrl的相对path
    @property (nonatomic) PARequestType requestType;  //P(这里没用到了)
    @property (nonatomic,strong) NSDictionary * params; //参数
    @property (nonatomic,strong) Class resultClass; //返回指定的类结构
    
    /**
     请求类
     
     @param path 相对于basePath的path
     @param dict 参数字典集
     @param resultClass 返回指定的类型
     */
    +(instancetype)requestWithPath:(NSString *)path params:(NSDictionary *)dict convertResultClass:(nullable Class)resultClass;
    

    主要那个方法的如下,当然到时可以添加请求参数签名,固定请求参数等在这个类里。

    /*功能
     1.这个类可以添加常用的默认请求参数:如token,app版本号等的处理
     2.处理请求数据的加密等
     */
    
    /**
     请求类
     
     @param path 相对于basePath的path
     @param dict 参数字典集
     @param resultClass 返回指定的类型
     */
    +(instancetype)requestWithPath:(NSString *)path params:(NSDictionary *)dict convertResultClass:(nullable Class)resultClass
    {
      return  [[self alloc] initReqestParamsWithPath:path params:dict convertResultClass:resultClass];
    }
    
    
    -(instancetype)initReqestParamsWithPath:(NSString *)path params:(NSDictionary *)params convertResultClass:(Class)resultClass
    {
        self = [super init];
        if (self) {
       
            self.path = [NSString stringWithFormat:@"%@%@",BaseHttpUrl, path];  //拼接完整的url
            self.params = params;
            self.resultClass = resultClass;
        }
        
        return self;
        
    }
    

    请求执行类PAHTTPService

    这里主要是一个单例,及请求的写法。在.h中,

    #import <Foundation/Foundation.h>
    #import <ReactiveObjC/ReactiveObjC.h>
    #import "PAHttpRequest.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface PAHTTPService :NSObject
    
    +(instancetype)shareInstance;
    
    //Post
    -(RACSignal *)getRequestNetWorkData:(PAHttpRequest *)request;
    
    //Get请求方法
    -(RACSignal *)postRequestNetWorkData:(PAHttpRequest *)request;
    
    -(instancetype)init   NS_UNAVAILABLE;
    +(instancetype)new  NS_UNAVAILABLE;
    -(id)copy NS_UNAVAILABLE;
    -(id)mutableCopy NS_UNAVAILABLE;
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    .m中:

    #import "PAHTTPService.h"
    #import "PAHttpConfigs.h"
    #import <AFNetworking/AFNetworking.h>
    #import "PAHttpResponse.h"
    
    
    NSString * const PAHttpErrorDomain = @"PAHTTPErrorDomain";
    NSString * const PAHttpErrorMessage = @"PAHTTPErroMassage";
    
    @interface PAHTTPService ()
    @property (nonatomic,strong) AFHTTPSessionManager * netManager;
    @end
    
    @implementation PAHTTPService
    
    +(instancetype)shareInstance
    {
        static PAHTTPService * service = nil;
        dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            service = [[self alloc] init];
            
        });
        return service;
        
    }
    
    -(void)configHttpService
    {
        
        self.netManager.requestSerializer.timeoutInterval = 10.f;
        self.netManager.responseSerializer.acceptableContentTypes =  [NSSet setWithObjects:@"application/json",
                                                           @"text/json",
                                                           @"text/javascript",
                                                           @"text/html",
                                                           @"text/plain",
                                                           @"text/html",
                                                           nil];
        self.netManager.requestSerializer.stringEncoding = NSUTF8StringEncoding;
        
       /*
        设置请求头的设置
        //
        [self.netManager.requestSerializer setValue:TOKEN forHTTPHeaderField:@"token-id"];
        
        */
        
       //// 安全策略
        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
        //allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
        //如果是需要验证自建证书,需要设置为YES
        securityPolicy.allowInvalidCertificates = YES;
        //validatesDomainName 是否需要验证域名,默认为YES;
        //假如证书的域名与你请求的域名不一致,需把该项设置为NO
        //主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
        securityPolicy.validatesDomainName = NO;
        
        self.netManager.securityPolicy = securityPolicy;
        
        /*
          添加网络监测等
         //
         [[AFNetworkReachabilityManager sharedManager] startMonitoring];
         [self.netManager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
         if (status == AFNetworkReachabilityStatusUnknown) {
         //未知网络
         
         }else if(status == AFNetworkReachabilityStatusNotReachable){
         //未连接
         }else{
         //有网络
         }
         }];
         
         */
        
    }
    
    -(RACSignal *)postRequestNetWorkData:(PAHttpRequest *)request
    {
        if (!request) {
            return  [RACSignal  error:[NSError errorWithDomain:PAHttpErrorDomain code:-1 userInfo:nil]];
        }
        
        
        RACSignal * signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            
            //创建网络请求
            NSLog(@"======== %@ %@",request.path,request.params);
            __block  NSURLSessionDataTask * task = nil;
            task = [self.netManager POST:request.path parameters:request.params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                
                PAHttpResponse * response = [[PAHttpResponse alloc] initWithResponseSuccess:responseObject code:0 resultClass:nil];
                NSLog(@"*******data : %@",response.responseObject);
                [subscriber sendNext:response.responseObject];
                [subscriber sendCompleted];
                
            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                  NSLog(@"报错了%@",error);
                [subscriber sendError:error];
            }];
            
            return [RACDisposable disposableWithBlock:^{
                [task cancel];  //取消
            }];
        }];
        
        return [signal replayLazily];  //多次订阅同样的信号,执行一次。
    }
    
    
    #pragma mark - Lazily
    
    -(AFHTTPSessionManager *)netManager
    {
        if (_netManager == nil) {
            _netManager = [AFHTTPSessionManager manager];
            [self configHttpService];
        }
        return _netManager;
    }
    
    
    @end
    
    

    请求数据处理类PAHttpResponse

    这类负责数据解析,响应错误的处理等。主要看各自的业务,这里只是粗略的写了一下。打算定义2个方法,一个成功输出的数据接口,一个失败输出的。在定义的属性时,只读。打算数据解析用MJExtension。

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface PAHttpResponse : NSObject
    
    @property(nonatomic,assign,readonly) BOOL isSuccess;  //请求成功的判断
    @property (nonatomic,assign,readonly) NSInteger code; //返回成功或失败的code
    @property (nonatomic,copy,readonly) NSString * message; //返回的信息
    
    @property (nonatomic,strong,readonly) id  responseObject; //原装的返回
    @property (nonatomic,strong,readonly) id  jsonClassObject; //解析成指定class的返回
    
    @property (nonatomic,strong,readonly) NSError *  resError; //可能服务器返回的错误信息
    
    -(instancetype)initWithResponseSuccess:(id) result code:(NSInteger )code resultClass:(nullable Class)resultClass;
    
    -(instancetype)initWithResponseError:(NSError *) err code:(NSInteger )code;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    
    /* 功能
      1. 对返回的数据的处理,如输出成指定的model
      2.处理返回的逻辑,如错误信息
     */
    
    #import "PAHttpResponse.h"
    @interface PAHttpResponse ()
    
    @property(nonatomic,assign,readwrite) BOOL isSuccess;  //请求成功的判断
    @property (nonatomic,assign,readwrite) NSInteger code; //返回成功或失败的code
    @property (nonatomic,copy,readwrite) NSString * message; //返回的信息
    
    @property (nonatomic,strong,readwrite) id  responseObject; //原装的返回
    @property (nonatomic,strong,readwrite) id  jsonClassObject; //解析成指定class的返回
    @property (nonatomic,strong,readwrite) NSError *  resError; //可能服务器返回的错误信息
    
    @end
    
    @implementation PAHttpResponse
    
    -(instancetype)initWithResponseSuccess:(id) result code:(NSInteger )code resultClass:(nullable Class)resultClass
    {
        if (self = [super init]) {
            self.isSuccess = YES; //这里看自己工程,有些要code == 0才算成功
            self.responseObject = result;
            self.code = code;
            
            if (resultClass) {  //如果要 解析成指定的类型
                /*
                //用MJExtension来解析
                id convertClass = nil;
                if ([result isKindOfClass:[NSArray class]]) {
                    NSMutableArray *resultList = [NSMutableArray array];
                    
                    for (id dataItem in result) {
                        [resultList addObject:[resultClass mj_objectWithKeyValues:dataItem]];
                    }
                    
                    convertClass = resultList;
                } else if ([result isKindOfClass:[NSNumber class]] || [data isKindOfClass:[NSString class]]) {
                    convertClass = result;
                } else if ([result isSubclassOfClass:[NSDictionary class]]) {
                    convertClass = resultClassData;
                } else {
                    
                    convertClass = [resultClass mj_objectWithKeyValues:result];
                }
                self.jsonClassObject = convertClass;
                */
            }
        }
        return self;
    }
    
    
    -(instancetype)initWithResponseError:(NSError *) err code:(NSInteger )code
    {
        if (self = [super init]) {
            self.isSuccess = NO;
            self.resError = err;
            self.code = code;
            
            //从error里拿message
    //        self.message = message;
        }
        return self;
    }
    
    
    @end
    
    

    请求常用宏的定义PAHttpConfigs

    用来放一些url 配置等

    1B584671-0987-48C3-891F-0F892DF7246D.png
    自己心里面大概就是这么想的,只是写了一下大概架子,具体的丰富功能加上去不是很难,这就是今天学得内容

    最后调用

    简单的调用如下,是可以正确拿到数据的,嘻嘻~,搞定。

    屏幕快照 2019-03-29 下午6.41.33.png
    后续:
    过了几天来看写的请求发现封装的还不够好,于是自己添加了个请求的工厂类方法:
    请求工厂方法.png
    .h方法
    /*
     工厂模式写全部的网络请求
     
     */
    
    
    #import <Foundation/Foundation.h>
    #import <ReactiveObjC/ReactiveObjC.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface PANetTools : NSObject
    
    +(RACSignal *)requestHotRoom;
    
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    .m

    #import "PANetTools.h"
    #import "PAHttpRequest.h"
    #import <ReactiveObjC/ReactiveObjC.h>
    #import "PAHttpConfigs.h"
    #import "PAHTTPService.h"
    
    
    @implementation PANetTools
    
    +(RACSignal *)requestHotRoom
    {
        NSMutableDictionary * paras = [NSMutableDictionary dictionary];
        PAHttpRequest * request = [PAHttpRequest requestWithPath:HotLivePath params:paras convertResultClass:nil];
        return  [[PAHTTPService shareInstance]postRequestNetWorkData:request];
    }
    
    
    @end
    

    最后调用请求方法如下:使用更加简单,嘻嘻~

      [[twoBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
            @strongify(self);
            [[PANetTools requestHotRoom] subscribeNext:^(id  _Nullable x) {
                NSLog(@"=======数据:%@",x);
            }error:^(NSError * _Nullable error) {
                NSLog(@"=======s失败:%@",error);
            }];
            
        }];
    

    总结

    • 在学RAC的过程中,网络的封装基本就可以了。感觉比较深的是怎么用信号来写。网上看好多文章不如自己边摸索边写。
    • 后期打算用RAC 写MVVM在工程中的运用。加油!

    相关文章

      网友评论

        本文标题:菜鸟用RAC基于AFNetworking 3.0网络的封装搭建记

        本文链接:https://www.haomeiwen.com/subject/scsrbqtx.html