前言
这几天自己在学习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等宏的文件
这几个文件的打算功能
- 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 配置等
自己心里面大概就是这么想的,只是写了一下大概架子,具体的丰富功能加上去不是很难,这就是今天学得内容
最后调用
简单的调用如下,是可以正确拿到数据的,嘻嘻~,搞定。
后续:
过了几天来看写的请求发现封装的还不够好,于是自己添加了个请求的工厂类方法:
.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在工程中的运用。加油!