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

前言

这几天自己在学习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 
#import 
#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 
#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  _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 

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 
#import 

NS_ASSUME_NONNULL_BEGIN

@interface PANetTools : NSObject

+(RACSignal *)requestHotRoom;


@end

NS_ASSUME_NONNULL_END

.m

#import "PANetTools.h"
#import "PAHttpRequest.h"
#import 
#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网络的封装搭建记录(四))