使用 ReactiveCocoa 重试命令

Retrying commands with ReactiveCocoa

我正在尝试将我的 iOS 应用程序重构为 ReactiveCocoa 和 ReactiveViewModel,并且我正在努力尝试找出几个最佳实践。

我将把它归结为一个简单的用例——我要推送一个加载一些数据并将其推送到 table 视图中的视图控制器。如果端点调用因任何原因失败,我想在屏幕上显示一个带有重试按钮的视图。

我目前正在使用它,但它看起来有点脏。我觉得一定有更好的方法——我这样做是否正确?

在我的 ViewModel 的 init 方法中,我正在创建我的命令,该命令会在 ViewModel 激活后立即调用。

// create the command to load the data
@weakify(self);
self.loadStationsCommand = [[RACCommand alloc] initWithSignalBlock:^(RACSignal *(id input) {
    @strongify(self);
    return [RACSignal createSignal:^(RACDisposable *(id<RACSubscriber subscriber) {
        // load data from my API endpoint
        ...
        BOOL succeeded = ...;
        if (succeeded) {
            [subscriber sendNext:nil];
            [subscriber sendCompleted:nil];
        } else {
            // failed
            [subscriber sendError:nil];   
        }
        return nil;
    }
}];

// start the command when the ViewModel's ready
[self.didBecomeActiveSignal subscribeNext:^(id x) {
    @strongify(self);
    [self.loadStationsCommand execute:nil];
}];

在我的 UIViewController 中,我通过 -

订阅命令
[self.viewModel.loadStationsCommand.executionSignals subscribeNext:^(RACSignal *loadStationsSignal) {
    [loadStationsSignal subscribeNext:^(id x) {
        // great, we got the data, reload the table view. 
        @strongify(self);
        [self.tableView reloadData];
    } error:^(NSError *error) {
        // THIS NEVER GETS CALLED?!
    }];
}];

[self.viewModel.loadStationsCommand.errors subscribeNext:^(id x) {
    // I actually get my error here. 
    // Show view/popup to retry the endpoint.
    // I can do this via [self.viewModel.loadStationsCommand execute:nil]; which seems a bit dirty too.
}];

我一定对RACCommand的工作原理有一些误解,或者至少我觉得我没有尽可能干净地做这件事。

为什么我的 loadStationsSignal 上的错误块没有被调用?为什么我需要订阅 executionCommand.errors

有没有更好的方法?

RACCommand处理错误是正确的方法。正如您在 docs 中所读到的,使用 executionSignals:

时不会发送内部信号错误
 Errors will be automatically caught upon the inner signals, and sent upon
 `errors` instead. If you _want_ to receive inner errors, use -execute: or
 -[RACSignal materialize].

您还可以使用 RAC 添加到 UIButton 并将 self.viewModel.loadStationsCommand 绑定到 retry 按钮的 rac_command

有一个很好的 article which explains RACCommand 并展示了一些可以与之一起使用的有趣模式。