菜鸟用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 <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在工程中的运用。加油!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,010评论 4 62
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,081评论 1 32
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,846评论 6 13
  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,160评论 1 23
  • 闷热潮湿会让人心烦意乱,静下心来的时候又开始无穷怀念。 喝点小酒,在安适的空调温度下不觉睡去,梦中回到故乡,回到儿...
    烈酒长歌阅读 381评论 2 2