ReactiveCocoa

时间:2015-05-14 19:04:04

标签: ios objective-c timer reactive-cocoa

我是ReactiveCocoa的新手,有一个问题我还没找到解决办法。我的应用程序中有一个网络请求,它返回要编码的数据,QR码只有30秒有效。网络请求返回RACSignal,我将要在该信号中编码的数据发送到我的视图模型。在视图模型中,我将该数据映射到QR图像,并将其作为视图模型界面中的属性公开。创建QR图像后,我想更新一个timeLeftString属性,说明“此代码仅在30秒内有效”,但秒数会随着时间的推移而变化,在完成30秒后,我想制作另一个请求获取另一个有效30秒的QR码数据,然后再完成另一个请求获取有效30秒的获取数据......直到屏幕被解除。我该如何实施呢?

目前我有这个来获取数据:

- (RACSignal *)newPaymentSignal
{
    @weakify(self);
    return [[[[APIManager sharedManager] newPayment] map:^id(NSString *paymentToken) {

        ZXMultiFormatWriter *writer = [ZXMultiFormatWriter writer];
        ZXBitMatrix *result =
            [writer encode:paymentToken format:kBarcodeFormatQRCode width:250 height:250 error:nil];

        if (!result) {
            return nil;
        }

        CGImageRef cgImage = [[ZXImage imageWithMatrix:result] cgimage];
        UIImage *image = [UIImage imageWithCGImage:cgImage];
        return UIImagePNGRepresentation(image);
    }] doNext:^(NSData *data) {
        @strongify(self);
        self.qrImageData = data;
    }];
}

这对于计时器

- (RACSignal *)timeRemainingSignal
{
    @weakify(self);
    return [[[RACSignal interval:0.5 onScheduler:[RACScheduler scheduler]]  //
        startWith:[NSDate date]]                                            //
        initially:^{
            @strongify(self);
            self.expiryDate = [[NSDate date] dateByAddingTimeInterval:30];
        }];
}

流程是:从api获取数据,启动计时器,当时间到了,发出新请求以获取新数据并再次启动计时器......并永远重复此步骤。

1-从API获取数据后如何启动计时器?

2-如何让这个流程永远重复?

3-如何在30秒完成之前停止计时器,如果用户点击用户界面上的按钮,则从头开始流程?

4-我有一个expiryDate属性,这个属性在当前日期增加了30秒,因为我认为我将采用expiryDate[NSDate date]的差异来决定时间是否到了 - 有没有更好的方法来实现这个?

5-当屏幕被解除时(或者,当用户点击另一个按钮时),我如何在永久重复时取消流量并取消订阅所有内容?

非常感谢您的回答。

1 个答案:

答案 0 :(得分:5)

我认为这个难题的缺失部分是非常有用的flattenMap运算符。它基本上用来自其返回信号的nexts替换其输入信号中的任何nexts。

这是解决问题的一种方法(我用一个发送字符串的简单信号替换了你的newPaymentSignal方法):

- (RACSignal *)newPaymentSignal
{
    return [[RACSignal return:@"token"] delay:2];
}

- (void)start
{
    NSInteger refreshInterval = 30;

    RACSignal *refreshTokenTimerSignal =
        [[RACSignal interval:refreshInterval onScheduler:[RACScheduler mainThreadScheduler]]
            startWith:[NSDate date]];

    [[[[refreshTokenTimerSignal
        flattenMap:^RACStream *(id _)
        {
            return [self newPaymentSignal];
        }]
        map:^NSDate *(NSString *paymentToken)
        {
            // display paymentToken here
            NSLog(@"%@", paymentToken);

            return [[NSDate date] dateByAddingTimeInterval:refreshInterval];
        }]
        flattenMap:^RACStream *(NSDate *expiryDate)
        {
            return [[[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]
                startWith:[NSDate date]]
                takeUntil:[refreshTokenTimerSignal skip:1]]
                map:^NSNumber *(NSDate *now)
                {
                    return @([expiryDate timeIntervalSinceDate:now]);
                }];
        }]
        subscribeNext:^(NSNumber *remaining)
        {
            // update timer readout here
            NSLog(@"%@", remaining);
        }];
}

每次外部refreshTokenTimerSignal触发时,它都会映射到一个新的newPaymentSignal,而当它返回一个值时,它会被映射到一个有效期,用于创建一个新的&# 34;内"定时器信号每秒发射一次。

内部计时器上的takeUntil运算符在外部刷新计时器发送下一个计时器后立即完成该信号。

(这里有一件奇怪的事情就是我必须在skip:1添加一个refreshTokenTimerSignal,否则内部计时器永远不会开始。即使没有{{1}我也希望它能工作也许更精通RAC内部的人可以解释为什么会这样。)

为了打破外部信号流以响应各种事件,您可以尝试在其上使用skip:1takeUntil