看完文檔后,似乎方法都知道怎么回事兒,但是應(yīng)用到項目上就無從下手,這篇文章就是來說一說項目實戰(zhàn)的例子。本文就綜合網(wǎng)上的文章和我平時遇到的問題來一一梳理一下,有一部分會是從其他地方引用而來,我會在文章下方說明出處。
實戰(zhàn)1:網(wǎng)絡(luò)下載圖片完成后 按鈕才可以點擊
-(void)btnAvliableWhenImgOK{
// 觀察img 是否修改,如果修改就會觸發(fā)
RACSignal * imagAvaibaleSignal = [RACObserve(self, self.imageView.image) map:^id(id value) {
return value ? @YES : @NO;
}];
[imagAvaibaleSignal subscribeNext:^(id x) {
NSLog(@"xx =%@",x);
}];
self.shareBtn.rac_command = [[RACCommand alloc] initWithEnabled:imagAvaibaleSignal signalBlock:^RACSignal *(id input) {
// do share logic
NSLog(@"input =%@",input);
return [RACSignal empty];// 必須返回一個信號,不能返回nil
}];
// 一個command 需要execute 才能觸發(fā)執(zhí)行 但是和btn綁定的command不需要
// [self.shareBtn.rac_command execute:@"100"];
/*
2016-02-19 11:14:25.359 JFReactive[26455:2201124] xx =0
2016-02-19 11:14:37.216 JFReactive[26455:2201124] xx =1
2016-02-19 11:16:21.597 JFReactive[26455:2201124] input =<UIButton: 0x7f9d2bd6fc60; frame = (93 330; 151 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7f9d2bd6bcc0>> */
}
我們使用RACObserver()
觀察self.imgView.img,然后使用map操作如果有值則返回yes,否則返回no,接下來我們使用RACCommand
使用imgAvailableSigna作為參數(shù)初始化一個RACCommand
并賦值給shareBtn.rac_command.
在運行上述代碼是img為nil 所以shareBtn enable = NO
在另外一個方法中下載圖片 使得 self.imgView.img = img, shareBtn的enable = yes
實現(xiàn)2: 使用rac_signalForSelector 實現(xiàn)協(xié)議方法
當(dāng)selector 執(zhí)行完后會發(fā)送next事件
[[self rac_signalForSelector:@selector(scrollViewDidEndDecelerating:) fromProtocol:@protocol(UIScrollViewDelegate)] subscribeNext:^(RACTuple *tuple) {
// do something
}];
[[self rac_signalForSelector:@selector(scrollViewDidScroll:) fromProtocol:@protocol(UIScrollViewDelegate)] subscribeNext:^(RACTuple *tuple) {
// do something
}];
實戰(zhàn)3: 網(wǎng)絡(luò)請求失敗后再發(fā)起一次請求
一般情況下,我們會遇到網(wǎng)絡(luò)請求失敗,但是失敗的原因有很多,總之我們還想再試一次,怎么辦呢?按照傳統(tǒng)的邏輯要定義一個標(biāo)簽,如果成功則返回該標(biāo)簽的值為yes,否則返回no,再次發(fā)起請求。好麻煩是不是,如果是RAC,就簡單了很多
-(void)retry{
__block int flag = 0;
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
if (flag == 4){
[subscriber sendNext:@"1"];
[subscriber sendCompleted];
}else{
flag ++;
NSLog(@"flag= %d",flag);
[subscriber sendError:[NSError errorWithDomain:@"myerror " code:100 userInfo:nil]];
}
return nil;
}];
[[signal retry:5]subscribeNext:^(id x) {
NSLog(@"xxxx =%@",x);
}];
}
是不是很簡單,retry(count) 可以指定任意數(shù)字,直到我們獲取正確的結(jié)果或者到達(dá)指定的count次數(shù)
實戰(zhàn)4:發(fā)送請求發(fā)現(xiàn)lostToken了
例如在請求我的投資數(shù)據(jù)(reqInvestAPI)發(fā)現(xiàn)token過期了,傳統(tǒng)的做法是在發(fā)送請求之前先去請求token(reqTokenAPI),等token回來后再發(fā)reqInvestAPI,噢,LadyGaga,你好嗎?
RACSignal *requestSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// suppose first time send request, access token is expired or invalid
// and next time it is correct.
// the block will be triggered twice.
static BOOL isFirstTime = 0;
NSString *url = @"http://httpbin.org/ip";
if (!isFirstTime) {
url = @"http://nonexists.com/error";
isFirstTime = 1;
}
NSLog(@"url:%@", url);
[[AFHTTPRequestOperationManager manager] GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
[subscriber sendNext:responseObject];
[subscriber sendCompleted];
NSLog(@"subscriber sendcompleted");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"subscriber send error");
[subscriber sendError:error];
}];
return nil;
}];
self.labelForName.text = @"sending request...";
//Subscribes to the returned signal when an error occurs.
[[requestSignal catch:^RACSignal *(NSError *error) {// requestSignal 發(fā)送error 觸發(fā) catch{} catch 中返回的signal 發(fā)送next 在subcribeNext接收后,再追加一次requestSignal
self.labelForName.text = @"oops, invalid access token";
NSLog(@"catch ....");
// 模擬獲取token的請求,然后concat requestSignal,再次發(fā)送之前的請求
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[subscriber sendNext:@YES];
NSLog(@"subscriber sendNext...");
[subscriber sendCompleted];
});
return nil;
}]concat:requestSignal];
}] subscribeNext:^(id x) {
NSLog(@"next =%@",x);
if ([x isKindOfClass:[NSDictionary class]]) {
self.labelForName.text = [NSString stringWithFormat:@"result:%@", x[@"origin"]];
}
} completed:^{
NSLog(@"completed");
}];
我們先創(chuàng)建了一個requestSignal,在這個signal中我們會先發(fā)送一次失敗的請求,然后被catch,catch方法中返回一個新的信號會被重新訂閱,在該信號中模擬網(wǎng)絡(luò)請求獲取token,然后再改請求token的signal中concat之前的信號(相當(dāng)于在此發(fā)送之前的請求)
實戰(zhàn)5:根據(jù)搜索框的文字進(jìn)行實時搜索
我們在用tmall 和 京東的app,會發(fā)現(xiàn)搜索框的結(jié)果會根據(jù)輸入的文字動態(tài)更新,這個放到我們的實際需求中會發(fā)現(xiàn),只要有用戶輸入的文字進(jìn)行改變我們就去發(fā)送請求,當(dāng)用戶前后兩次輸入間隔很短,我們發(fā)送了兩次請求,之前的請求還沒返回下一次的已經(jīng)發(fā)送了,這勢必會造成服務(wù)器的壓力,另外第一次的請求也要拋棄掉。如果放到RAC
該如何處理呢?
[[[[[[self.textField.rac_textSignal throttle:1]distinctUntilChanged]ignore:@""] map:^id(id value) {
NSLog(@"value =%@",value);
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// network request
[subscriber sendNext:value];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
// cancel request
}];
}];
}]switchToLatest] subscribeNext:^(id x) {// 如果不switchToLastest 則返回一個signal
NSLog(@"x = %@",x);
}];
我們在map中根據(jù)輸入框的值 模擬發(fā)送網(wǎng)絡(luò)請求。
throttle的參數(shù)是一個NSTimerInternal,指定一個時間間隔。
查看該方法的文檔知道,在interval間隔內(nèi)如果是已經(jīng)接收到下一個next 事件,就會拋棄前一個事件。
在這里我們設(shè)置間隔為1s,distinctchanged方法是檢測前后兩次事件的值是否改變,如果改變才會觸發(fā)接下來的事件
實戰(zhàn)6: 多個信號組合
RACSignal * singal = [RACSignal
combineLatest:@[ RACObserve(self, self.model.age), RACObserve(self, self.model.name) ]
reduce:^(NSString *password, NSString *passwordConfirm) {
return @([passwordConfirm isEqualToString:password]);
}];
[singal subscribeNext:^(id x) {
NSLog(@"xx =%@",x) ;
}];
/* // 如果是直接RAC()的話 自動進(jìn)行了subscribeNext:
RAC(self, self.textField.enabled) =[RACSignal
combineLatest:@[ RACObserve(self, self.model.age), RACObserve(self, self.model.name) ]
reduce:^(NSString *password, NSString *passwordConfirm) {
return @([passwordConfirm isEqualToString:password]);
}];
*/
實戰(zhàn)7:使用Command 模擬登錄
self.loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
//模擬login signal
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1000"];
[subscriber sendCompleted];
return nil;
}];
}];
// -executionSignals returns a signal that includes the signals returned from
// the above block, one for each time the command is executed.
[self.loginCommand.executionSignals subscribeNext:^(RACSignal *loginSignal) {
// Log a message whenever we log in successfully.
[loginSignal subscribeNext:^(id x) {
NSLog(@"xxx %@",x);
}];
[loginSignal subscribeCompleted:^{
NSLog(@"Logged in successfully!");
}];
}];
// [self.loginCommand.executionSignals.switchToLatest subscribeNext:^(id x) {
// NSLog(@"xx =%@",x);
// }];
// Executes the login command when the button is pressed. 按鈕點擊觸發(fā)
self.shareBtn.rac_command = self.loginCommand;
后續(xù)的再補(bǔ)充吧...