使用 ReactiveCocoa 在网络 API 调用失败时请求重试

Ask for retry upon a failure of the network API call by using ReactiveCocoa

我在我的 iOS 应用程序中使用 ReactiveCocoa 来处理网络 API 请求。如果我想显示一个 UIAlertView 并要求用户单击重试按钮,并且仅当用户单击重试按钮时才会重试同一个 API 调用,那应该怎么做?

- (RACSignal*) fetchImportantData {
    return [RACSignal createSignal: ^RACDisposable*(id<RACSubscriber> subscriber) {
        return [apiCall subscribeNext:^(id x) {
            [subscriber sendNext:x]; 
            [subscriber sendCompleted];
        } error:^(NSError *error) {
            [subscriber sendError:error];
        }];
    }];
}

这应该可以解决问题。

RACSignal * catchSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    UIAlertView * alertView = [[UIAlertView alloc]
                               initWithTitle:@"Try again"
                               message:@""
                               delegate:nil
                               cancelButtonTitle:@"No"
                               otherButtonTitles:@"Yes", nil];
    [alertView.rac_buttonClickedSignal subscribeNext:^(NSNumber * buttonIndex) {
        if (buttonIndex.integerValue != alertView.cancelButtonIndex)
        {
            [subscriber sendCompleted];
        }
        else
        {
            [subscriber sendError:nil];
        }
    }];
    [alertView show];
    return nil;
}];

[[[[[self fetchImportantData] catchTo:catchSignal] repeat] take:1] subscribeNext:^(id x) {
    NSLog(@"NEXT: %@", x);
} error:^(NSError *error) {
    NSLog(@"ERROR: %@", error);
} completed:^{
    NSLog(@"COMPLETED");
}];

所以这里发生的是来自 fetchImportantData 的错误被 catchTo: 捕获,然后信号被该信号发送的任何内容替换(有点像 flattenMap: ,但对于错误)。由于我们现在有了控制权,我们可以将 sendCompleted 连接到 "Yes" 按钮并使用 repeat 让信号在完成后重复,同时将 sendError: 连接到 "No" 按钮,以便我们可以在用户不想重试时立即停止所有订阅。

fetchImportantData 最终 returns 一个非错误时,它将被发送并完全跳过我们的 catchTo: 块,并且信号将完成感谢我们的 take:1.