ReactiveCocoa - 捕获错误时,无法再次触发信号

时间:2018-02-06 06:45:01

标签: objective-c io reactive-cocoa

我尝试使用ReactiveCocoa序列化几个异步网络:

[[[[self.profileView.signInButton rac_signalForControlEvents:UIControlEventTouchUpInside] flattenMap:^__kindof RACSignal * _Nullable(__kindof UIControl * _Nullable value) {
    @strongify(self)
    return [self.viewModel signInSignal];
}] flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
    return [self.viewModel userInfoSignal];
}] subscribeNext:^(id  _Nullable x) {

} error:^(NSError * _Nullable error) {
    NSLog(@"error");
}];

如果没有发生网络错误,它会起作用。但是,当发生任何错误并调用错误块时,我再次单击“signInButton”但它不再起作用。我想知道原因以及如何解决它。谢谢!

1 个答案:

答案 0 :(得分:1)

原因是Error事件立即通过运营商链转发,Error个事件终止订阅。

处理此问题的一种方法是使用retry运算符,该运算符在发生错误时重新订阅信号。下面是使用retry

的示例的修改版本
@weakify(self);
[[[[[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] flattenMap:^__kindof RACSignal * _Nullable(__kindof UIControl * _Nullable value) {
  @strongify(self)
  return [self.viewModel signInSignal];
}] flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
  return [self.viewModel userInfoSignal];
}] doError:^(NSError * _Nonnull error) {
  NSLog(@"Inner Error: %@", error);
}] retry:2]
 subscribeNext:^(id  _Nullable x) {
  NSLog(@"Next: %@", x);
} error:^(NSError * _Nullable error) {
  NSLog(@"Error: %@", error);
}];

这里使用了retryCount的变体 - 它会在将错误传播到外部之前重试2次。此外,doError用于在发生错误时执行日志记录调用作为副作用(注意:这必须是之前 retry运算符)

鉴于signInSignaluserInfoSignal将在100%的时间内产生错误,按3次按钮将产生以下输出

  

内部错误

     

内部错误

     

内部错误

     

错误

RACCommand

我建议你看一下另一个解决方案是将逻辑封装到RACCommand(在你的viewModel中):

_signInCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
  return [self.signInSignal flattenMap:^RACSignal *(id value) {
    return self.userInfoSignal;
  }];
}];

您可以将此链接到您的按钮,只需:

self.button.rac_command = self.viewModel.signInCommand;

这样,您无需手动处理重试:每次单击按钮都会调用一次命令,该命令可以成功完成或错误。

您可以通过命令特殊信号来处理命令的副作用:

[[self.viewModel.signInCommand errors] subscribeNext:^(NSError * _Nullable x) {
  NSLog(@"Error: %@", x);
}];

[self.viewModel.signInCommand.executionSignals subscribeNext:^(id  _Nullable signal) {
  [signal subscribeNext:^(id  _Nullable x) {
    NSLog(@"Next: %@", x);
  }];
}];

以这种方式使用RACCommand的一大好处是,它会在命令运行时自动禁用按钮,因此,如果您的signInSignaluserInfoSignal需要一些时间,那么按钮将是在此期间自动禁用,以便用户在其已经运行时无法再次启动该操作。