前言
這幾天自己在學(xué)習(xí)RAC,學(xué)習(xí)了幾天對RAC有了一定基礎(chǔ)了解词爬,如之前寫的
菜鳥開始學(xué)習(xí)ReactiveObjc運(yùn)用的記錄(二)
菜鳥運(yùn)用RAC對replay,replayLast,replayLazily記錄(三)
看著前面寫的,雖然是基礎(chǔ)率挣,但自己堅(jiān)持記錄下來枢里,心里還是有滿滿的成就感的,哈哈~
好,回到我今天學(xué)到的東西淳蔼。在學(xué)習(xí)RAC的幾天,心里就想著怎么把RAC用到自己的工程中裁眯,因?yàn)閷?shí)踐后才是自己的東西肖方!
gitHub代碼地址:https://github.com/CgtST/ReactiveObjCDemo.git
怎么開始封裝呢
自己以前的工程是用的AFNetWorking網(wǎng)絡(luò)庫,一般是寫個(gè)單例對AFNetWorking的二次封裝未状。但如果用萬物皆信號的RAC又怎么處理俯画,其實(shí)在心里琢磨了很久。網(wǎng)上有用RAC中的 command來表示請求來執(zhí)行的過程的司草,有用的以前的網(wǎng)絡(luò)類中直接改成Singnal方式的艰垂。 這里我用RACSignal replayzily 來處理泡仗。于是用xcode新建工程,邊寫邊思考來寫猜憎。
心里琢磨著建立幾個(gè)類呢娩怎?建立了一個(gè)請求之前的處理類,請求執(zhí)行類胰柑,數(shù)據(jù)返回處理類,后面還有個(gè)寫base url等宏的文件
這幾個(gè)文件的打算功能
- PAHttpRequest: 處理網(wǎng)絡(luò)請求參數(shù)類型截亦,如每個(gè)請求有固定的參數(shù)(app 版本號,客戶端ios等)柬讨,處理base url與path路徑的拼接崩瓤,以及請求參數(shù)的加密等內(nèi)容,解析成指定類的參數(shù)踩官;
- PAHTTPService:對AF的二次封裝處理Get却桶,Post,文件上傳等功能了。
- PAHTTPResponse:解析成指定的數(shù)據(jù)模型蔗牡,返回?cái)?shù)據(jù)的處理颖系,錯(cuò)誤處理等
請求參數(shù)類PAHttpRequest
在PAHttpRequest.h中如下,自己只定義了一個(gè)方法想到時(shí)返回指定的數(shù)據(jù)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; //參數(shù)
@property (nonatomic,strong) Class resultClass; //返回指定的類結(jié)構(gòu)
/**
請求類
@param path 相對于basePath的path
@param dict 參數(shù)字典集
@param resultClass 返回指定的類型
*/
+(instancetype)requestWithPath:(NSString *)path params:(NSDictionary *)dict convertResultClass:(nullable Class)resultClass;
主要那個(gè)方法的如下嘁扼,當(dāng)然到時(shí)可以添加請求參數(shù)簽名,固定請求參數(shù)等在這個(gè)類里黔攒。
/*功能
1.這個(gè)類可以添加常用的默認(rèn)請求參數(shù):如token,app版本號等的處理
2.處理請求數(shù)據(jù)的加密等
*/
/**
請求類
@param path 相對于basePath的path
@param dict 參數(shù)字典集
@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;
}
請求執(zhí)行類PAHTTPService
這里主要是一個(gè)單例偷拔,及請求的寫法。在.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;
/*
設(shè)置請求頭的設(shè)置
//
[self.netManager.requestSerializer setValue:TOKEN forHTTPHeaderField:@"token-id"];
*/
//// 安全策略
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
//allowInvalidCertificates 是否允許無效證書(也就是自建的證書)莲绰,默認(rèn)為NO
//如果是需要驗(yàn)證自建證書,需要設(shè)置為YES
securityPolicy.allowInvalidCertificates = YES;
//validatesDomainName 是否需要驗(yàn)證域名姑丑,默認(rèn)為YES蛤签;
//假如證書的域名與你請求的域名不一致,需把該項(xiàng)設(shè)置為NO
//主要用于這種情況:客戶端請求的是子域名栅哀,而證書上的是另外一個(gè)域名震肮。因?yàn)镾SL證書上的域名是獨(dú)立的,假如證書上注冊的域名是www.google.com留拾,那么mail.google.com是無法驗(yàn)證通過的戳晌;當(dāng)然,有錢可以注冊通配符的域名*.google.com痴柔,但這個(gè)還是比較貴的沦偎。
securityPolicy.validatesDomainName = NO;
self.netManager.securityPolicy = securityPolicy;
/*
添加網(wǎng)絡(luò)監(jiān)測等
//
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
[self.netManager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (status == AFNetworkReachabilityStatusUnknown) {
//未知網(wǎng)絡(luò)
}else if(status == AFNetworkReachabilityStatusNotReachable){
//未連接
}else{
//有網(wǎng)絡(luò)
}
}];
*/
}
-(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) {
//創(chuàng)建網(wǎng)絡(luò)請求
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(@"報(bào)錯(cuò)了%@",error);
[subscriber sendError:error];
}];
return [RACDisposable disposableWithBlock:^{
[task cancel]; //取消
}];
}];
return [signal replayLazily]; //多次訂閱同樣的信號,執(zhí)行一次。
}
#pragma mark - Lazily
-(AFHTTPSessionManager *)netManager
{
if (_netManager == nil) {
_netManager = [AFHTTPSessionManager manager];
[self configHttpService];
}
return _netManager;
}
@end
請求數(shù)據(jù)處理類PAHttpResponse
這類負(fù)責(zé)數(shù)據(jù)解析豪嚎,響應(yīng)錯(cuò)誤的處理等搔驼。主要看各自的業(yè)務(wù),這里只是粗略的寫了一下侈询。打算定義2個(gè)方法舌涨,一個(gè)成功輸出的數(shù)據(jù)接口,一個(gè)失敗輸出的扔字。在定義的屬性時(shí)囊嘉,只讀。打算數(shù)據(jù)解析用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; //可能服務(wù)器返回的錯(cuò)誤信息
-(instancetype)initWithResponseSuccess:(id) result code:(NSInteger )code resultClass:(nullable Class)resultClass;
-(instancetype)initWithResponseError:(NSError *) err code:(NSInteger )code;
@end
NS_ASSUME_NONNULL_END
/* 功能
1. 對返回的數(shù)據(jù)的處理扭粱,如輸出成指定的model
2.處理返回的邏輯,如錯(cuò)誤信息
*/
#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; //可能服務(wù)器返回的錯(cuò)誤信息
@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 配置等
自己心里面大概就是這么想的焊刹,只是寫了一下大概架子系任,具體的豐富功能加上去不是很難恳蹲,這就是今天學(xué)得內(nèi)容
最后調(diào)用
簡單的調(diào)用如下,是可以正確拿到數(shù)據(jù)的俩滥,嘻嘻~嘉蕾,搞定。
后續(xù):
過了幾天來看寫的請求發(fā)現(xiàn)封裝的還不夠好霜旧,于是自己添加了個(gè)請求的工廠類方法:
.h方法
/*
工廠模式寫全部的網(wǎng)絡(luò)請求
*/
#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
最后調(diào)用請求方法如下:使用更加簡單错忱,嘻嘻~
[[twoBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self);
[[PANetTools requestHotRoom] subscribeNext:^(id _Nullable x) {
NSLog(@"=======數(shù)據(jù):%@",x);
}error:^(NSError * _Nullable error) {
NSLog(@"=======s失敗:%@",error);
}];
}];
總結(jié)
- 在學(xué)RAC的過程中挂据,網(wǎng)絡(luò)的封裝基本就可以了以清。感覺比較深的是怎么用信號來寫。網(wǎng)上看好多文章不如自己邊摸索邊寫崎逃。
- 后期打算用RAC 寫MVVM在工程中的運(yùn)用掷倔。加油!