美文网首页iOS开发iOS开发程序员
ReactiveCocoa项目实战例子

ReactiveCocoa项目实战例子

作者: 楼上那位 | 来源:发表于2016-02-19 17:35 被阅读1493次
    ReactiveCocoa项目实战例子

    看完文档后,似乎方法都知道怎么回事儿,但是应用到项目上就无从下手,这篇文章就是来说一说项目实战的例子。本文就综合网上的文章和我平时遇到的问题来一一梳理一下,有一部分会是从其他地方引用而来,我会在文章下方说明出处。

    实战1:网络下载图片完成后 按钮才可以点击
    -(void)btnAvliableWhenImgOK{
        //  观察img 是否修改,如果修改就会触发
        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 才能触发执行 但是和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作为参数初始化一个RACCommand并赋值给shareBtn.rac_command.
    在运行上述代码是img为nil 所以shareBtn enable = NO
    在另外一个方法中下载图片 使得 self.imgView.img = img, shareBtn的enable = yes

    实现2: 使用rac_signalForSelector 实现协议方法

    当selector 执行完后会发送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
        }];
    
    
    实战3: 网络请求失败后再发起一次请求

    一般情况下,我们会遇到网络请求失败,但是失败的原因有很多,总之我们还想再试一次,怎么办呢?按照传统的逻辑要定义一个标签,如果成功则返回该标签的值为yes,否则返回no,再次发起请求。好麻烦是不是,如果是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) 可以指定任意数字,直到我们获取正确的结果或者到达指定的count次数

    实战4:发送请求发现lostToken了

    例如在请求我的投资数据(reqInvestAPI)发现token过期了,传统的做法是在发送请求之前先去请求token(reqTokenAPI),等token回来后再发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 发送error 触发 catch{}  catch 中返回的signal 发送next 在subcribeNext接收后,再追加一次requestSignal
            self.labelForName.text = @"oops, invalid access token";
            NSLog(@"catch ....");
            // 模拟获取token的请求,然后concat requestSignal,再次发送之前的请求
            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");
        }];
        
    

    我们先创建了一个requestSignal,在这个signal中我们会先发送一次失败的请求,然后被catch,catch方法中返回一个新的信号会被重新订阅,在该信号中模拟网络请求获取token,然后再改请求token的signal中concat之前的信号(相当于在此发送之前的请求)

    实战5:根据搜索框的文字进行实时搜索

    我们在用tmall 和 京东的app,会发现搜索框的结果会根据输入的文字动态更新,这个放到我们的实际需求中会发现,只要有用户输入的文字进行改变我们就去发送请求,当用户前后两次输入间隔很短,我们发送了两次请求,之前的请求还没返回下一次的已经发送了,这势必会造成服务器的压力,另外第一次的请求也要抛弃掉。如果放到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中根据输入框的值 模拟发送网络请求。
    throttle的参数是一个NSTimerInternal,指定一个时间间隔。
    查看该方法的文档知道,在interval间隔内如果是已经接收到下一个next 事件,就会抛弃前一个事件。
    在这里我们设置间隔为1s,distinctchanged方法是检测前后两次事件的值是否改变,如果改变才会触发接下来的事件

    实战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()的话 自动进行了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]);
                                            }];
         */
    
    实战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. 按钮点击触发
        self.shareBtn.rac_command = self.loginCommand;
    

    后续的再补充吧...

    参考:
    http://www.jianshu.com/p/e10e5ca413b7

    相关文章

      网友评论

      本文标题:ReactiveCocoa项目实战例子

      本文链接:https://www.haomeiwen.com/subject/noxvkttx.html