Reactive Cocoa 中的超时实现是否正确?

Is this implementation of a timeout in Reactive Cocoa correct?

我做了一个连接到 ReactiveCocoa 中的按钮的登录。即使我测试了这段代码,它似乎可以正常工作,但我不确定我是否做对了。 成功时登录信号 returns "next",其他情况下登录信号 "error"。由于我不希望按钮因错误而被取消订阅,因此我使用了 catch 函数。

我想要的是:如果未触发 loginSignal,我希望在 2 秒后触发超时。这是否正确完成?是不是也做对了"reactive way"?

[[[[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside]
        doNext:^(id x) {
            [self disableUI];
        }]
        flattenMap:^(id value) {
            return [[[[[self.login loginSignalWithUsername:self.usernameTextField.text
                                               andPassword:self.passwordTextField.text]
                    catch:^RACSignal *(NSError *error) {
                        [self enableUI];
                        [self showAlertWithTitle:NSLocalizedString(@"ERROR_TITLE", @"Error")
                                         message:NSLocalizedString(@"LOGIN_FAILURE", @"Login not successful.")];
                        return [RACSignal empty];
                    }]

                    deliverOn:[RACScheduler mainThreadScheduler]]
                    timeout:2.0 onScheduler:[RACScheduler mainThreadScheduler]]
                    catch:^RACSignal *(NSError *error) {
                        [self enableUI];
                        [self showAlertWithTitle:NSLocalizedString(@"TIMEOUT_TITLE", @"Timeout occured")
                                         message:NSLocalizedString(@"REQUEST_NOT_POSSIBLE", @"Server request failed")];
                        return [RACSignal empty];
                    }];
        }]
        subscribeNext:^(id x) {
            [self enableUI];
            // Go to next page after login
        }];

我认为您应该使用 RACCommand,它是将信号绑定到 UI 的一个很好的集线器:

RACCommand* command = [[RACCommand alloc] initWithSignalBlock:^(id _) {
    return [[[self.login loginSignalWithUsername:self.username password:self.password] 
            doCompleted:^{
                // move to next screen
            }]
            timeout:2.0 onScheduler:[RACScheduler mainThreadScheduler]];
}];
self.button.rac_command = command;

然后您可以使用命令 "errors" 信号处理任何错误(登录或超时):

[[command errors] subscribeNext:^(NSError* err) {
    // display error "err" to the user
}];

信号在执行时会自动禁用按钮。如果您需要禁用 UI 的其他部分,您可以使用命令的 "executing" 信号。

[[command executing] subscribeNext:^(NSNumber* executing) {
    if([executing boolValue]) {
        [self disableUI];
    } else {
        [self enableUI];
    }
}];

// bonus note: if your enableUI method took a BOOL you could lift it in one line :
[self rac_liftSelector:@selector(enableUI:) withSignals:[command executing], nil];

here is a blog article talking about RACCommands