我在新的 iOS 应用程序中使用 ReactiveCocoa。我是响应式(Reactive)编程的新手,所以我仍在尝试了解什么是链接信号的正确方法。 现在我有以下“使用 Twitter 登录”按钮的流程。
ALTUserManager
类具有以下方法来管理整个登录阶段,方法是调用库中的一些函数,该库提供 Twitter 登录面板并执行所有 OAuth 操作:
- (RACSignal *)loginTwitter:(UIViewController *)vc {
RACSignal *loginSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[ALTTwitter sharedInstance]isLoggedIn:^(BOOL loggedIn) {
if(loggedIn){
[subscriber sendCompleted];
}
else{
[[ALTTwitter sharedInstance]login:vc andSuccess:^{
[subscriber sendCompleted];
} failure:^(NSString *error) {
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[NSLocalizedDescriptionKey] = error;
[subscriber sendError:[NSError errorWithDomain:@"" code:1 userInfo:userInfo]];
}];
}
}];
return nil;
}];
return loginSignal;
}
我使用的是 MVVM 模式,所以在我的 ViewModel 中,我在其 init 方法中添加了以下命令:
self.twitterLoginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [[ALTUserManager sharedInstance] loginTwitter:nil];
}];
在我的 View Controller 中,我正在处理表示逻辑,我在显示进度 HUD 时阻塞界面,并最终报告错误或如果一切正常则跳过登录屏幕:
self.twBtn.rac_command = self.viewModel.twitterLoginCommand;
[self.viewModel.twitterLoginCommand.executionSignals subscribeNext:^(id x) {
NSLog(@"%@", x);
[x subscribeCompleted:^{
NSLog(@"%@", @"completed");
[ALTAlert wait:@""];
[[self.viewModel appLoginWithTwitter] subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
[ALTAlert dismiss];
[ALTAlert error:error.localizedDescription];
} completed:^{
[ALTAlert dismiss];
@strongify(self);
[self goToChart];
}];
}];
}];
[self.viewModel.twitterLoginCommand.errors subscribeNext:^(NSError *error) {
NSLog(@"Login error: %@", error);
[ALTAlert dismiss];
[ALTAlert error:error.localizedDescription];
}];
我很确定这可以用更好的方式重写。我主要关心的是 [x subscribeCompleted]
行。什么是正确的方法?
谢谢!
更新
我尝试将所有逻辑移动到 RACCommand
内的 ViewModel,但我仍然需要捕获 RACCommand
内发生的错误。
订阅 errors
信号不是一个选项,因为 RACCommand
仍然会返回 completed
事件,从而使我的表示逻辑无法判断是否一切顺利。
我还没有尝试在 RACCommand 中设置一个 BOOL 并在出现错误时产生副作用并在 View 中观察它。但无论如何,这种方法似乎有点老套。
最佳答案
您可以使用 then
帮助程序稍微简化嵌套,这将简化错误处理并防止单独的 twitterLoginCommand.errors
订阅:
[self.viewModel.twitterLoginCommand.executionSignals subscribeNext:^(id x) {
[x then:^{
NSLog(@"%@", @"completed");
[ALTAlert wait:@""];
return [self.viewModel appLoginWithTwitter];
}] subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
[ALTAlert dismiss];
[ALTAlert error:error.localizedDescription];
} completed:^{
[ALTAlert dismiss];
@strongify(self);
[self goToChart];
}];
}];
不过,这有点奇怪。因为如果 twitterLoginCommand
在 appLoginWithTwitter
信号完成之前再次触发,您可能会进入奇怪的状态。考虑到应用程序的其余部分,这可能是不可能的,但仅孤立地查看此代码块是我会担心的事情。
更好的做法可能是将 then
block 移动到 RACCommand
中,以确保这种情况永远不会发生(作为 RACCommand
在前一个执行完之前不会再次执行。)虽然没有看到更多代码,但我不能真正说这是否是一个合理的更改。
这是一件很难进一步清理的事情,因为它本身就有副作用。如果您为 ALTAlert
类创建一个响应式桥接器,您可以清除其中的许多订阅,因为您可以只说“查看这个信号中的信号,并让您的状态反射(reflect)它”。然后你可以只传递执行信号,而不必担心在这里做一些更粗暴的事情。
那么你唯一真正的副作用是 goToChart
,你可以做一些更简单的事情:
[[[self.viewModel.twitterLoginCommand.executionSignals flattenMap:^(id x) {
return [x materialize];
}] filter:^(RACEvent *event) {
return event.eventType == RACEventTypeCompleted;
}] subscribeNext:^(id x) {
@strongify(self);
[self goToChart];
}];
关于ios - 如何使用 Reactive Cocoa 以正确的方式链接信号?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29005652/