我是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-当屏幕被解除时(或者,当用户点击另一个按钮时),我如何在永久重复时取消流量并取消订阅所有内容?
非常感谢您的回答。
答案 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:1
和takeUntil
。