ReactiveCocoa項(xiàng)目實(shí)戰(zhàn)例子

圖片發(fā)自簡書App

看完文檔后,似乎方法都知道怎么回事兒,但是應(yīng)用到項(xiàng)目上就無從下手,這篇文章就是來說一說項(xiàng)目實(shí)戰(zhàn)的例子。本文就綜合網(wǎng)上的文章和我平時(shí)遇到的問題來一一梳理一下,有一部分會(huì)是從其他地方引用而來,我會(huì)在文章下方說明出處。

實(shí)戰(zhàn)1:網(wǎng)絡(luò)下載圖片完成后 按鈕才可以點(diǎn)擊
-(void)btnAvliableWhenImgOK{
    //  觀察img 是否修改,如果修改就會(huì)觸發(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];// 必須返回一個(gè)信號(hào),不能返回nil
    }];
    // 一個(gè)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ù)初始化一個(gè)RACCommand并賦值給shareBtn.rac_command.
在運(yùn)行上述代碼是img為nil 所以shareBtn enable = NO
在另外一個(gè)方法中下載圖片 使得 self.imgView.img = img, shareBtn的enable = yes

實(shí)現(xiàn)2: 使用rac_signalForSelector 實(shí)現(xiàn)協(xié)議方法

當(dāng)selector 執(zhí)行完后會(huì)發(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
    }];

實(shí)戰(zhàn)3: 網(wǎng)絡(luò)請(qǐng)求失敗后再發(fā)起一次請(qǐng)求

一般情況下,我們會(huì)遇到網(wǎng)絡(luò)請(qǐng)求失敗,但是失敗的原因有很多,總之我們還想再試一次,怎么辦呢?按照傳統(tǒng)的邏輯要定義一個(gè)標(biāo)簽,如果成功則返回該標(biāo)簽的值為yes,否則返回no,再次發(fā)起請(qǐng)求。好麻煩是不是,如果是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ù)

實(shí)戰(zhàn)4:發(fā)送請(qǐng)求發(fā)現(xiàn)lostToken了

例如在請(qǐng)求我的投資數(shù)據(jù)(reqInvestAPI)發(fā)現(xiàn)token過期了,傳統(tǒng)的做法是在發(fā)送請(qǐng)求之前先去請(qǐng)求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的請(qǐng)求,然后concat requestSignal,再次發(fā)送之前的請(qǐng)求
        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)建了一個(gè)requestSignal,在這個(gè)signal中我們會(huì)先發(fā)送一次失敗的請(qǐng)求,然后被catch,catch方法中返回一個(gè)新的信號(hào)會(huì)被重新訂閱,在該信號(hào)中模擬網(wǎng)絡(luò)請(qǐng)求獲取token,然后再改請(qǐng)求token的signal中concat之前的信號(hào)(相當(dāng)于在此發(fā)送之前的請(qǐng)求)

實(shí)戰(zhàn)5:根據(jù)搜索框的文字進(jìn)行實(shí)時(shí)搜索

我們?cè)谟胻mall 和 京東的app,會(huì)發(fā)現(xiàn)搜索框的結(jié)果會(huì)根據(jù)輸入的文字動(dòng)態(tài)更新,這個(gè)放到我們的實(shí)際需求中會(huì)發(fā)現(xiàn),只要有用戶輸入的文字進(jìn)行改變我們就去發(fā)送請(qǐng)求,當(dāng)用戶前后兩次輸入間隔很短,我們發(fā)送了兩次請(qǐng)求,之前的請(qǐng)求還沒返回下一次的已經(jīng)發(fā)送了,這勢(shì)必會(huì)造成服務(wù)器的壓力,另外第一次的請(qǐng)求也要拋棄掉。如果放到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 則返回一個(gè)signal
        
        NSLog(@"x = %@",x);
    }];

我們?cè)趍ap中根據(jù)輸入框的值 模擬發(fā)送網(wǎng)絡(luò)請(qǐng)求。
throttle的參數(shù)是一個(gè)NSTimerInternal,指定一個(gè)時(shí)間間隔。
查看該方法的文檔知道,在interval間隔內(nèi)如果是已經(jīng)接收到下一個(gè)next 事件,就會(huì)拋棄前一個(gè)事件。
在這里我們?cè)O(shè)置間隔為1s,distinctchanged方法是檢測前后兩次事件的值是否改變,如果改變才會(huì)觸發(fā)接下來的事件

實(shí)戰(zhàn)6: 多個(gè)信號(hào)組合
 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()的話 自動(dòng)進(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]);
                                        }];
     */
實(shí)戰(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. 按鈕點(diǎn)擊觸發(fā)
    self.shareBtn.rac_command = self.loginCommand;

后續(xù)的再補(bǔ)充吧...

參考:
http://www.itdecent.cn/p/e10e5ca413b7

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容