我在ReactiveCocoa世界中仍然相当新,我只想澄清这种常见情况。我注意到其他人在GitHub和SO上都在努力解决这个问题,但我仍然没有找到合适的答案。
以下示例确实有效,但我看到Justin Summers说订阅内订阅或订阅通常可能是代码味道。因此,我想在学习这个新范例时尝试避免坏习惯。
因此,示例(使用MVVM)非常简单:
代码。
1(ViewController):
RAC(self.loginButton, rac_command) = RACObserve(self, viewModel.loginCommand);
2(ViewModel):
self.loginCommand = [[RACCommand alloc] initWithEnabled:canLoginSignal
signalBlock:^RACSignal *(id input) {
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
BOOL success = [username isEqualToString:@"user"] && [password isEqualToString:@"password"];
// Doesn't really make any sense to use sendNext here, but lets include it to test whether we can handle it in our viewmodel or viewcontroller
[subscriber sendNext:@"test"];
if (success)
{
[subscriber sendCompleted];
} else {
[subscriber sendError:nil];
}
// Cannot cancel request
return nil;
}] materialize];
}];
3(ViewController):
[self.viewModel.loginCommand.executionSignals subscribeNext:^(RACSignal *execution) {
[[execution dematerialize] subscribeNext:^(id value) {
NSLog(@"Value: %@", value);
} error:^(NSError *error) {
NSLog(@"Error: %@", error);
} completed:^{
NSLog(@"Completed");
}];
}];
你会如何以更多的ReactiveCococa-kind-a方式做到这一点?
答案 0 :(得分:8)
RACCommand
方式有效,值来自executionSignals
信号,来自errors
信号的错误和完成,以及那些人可能使用的-materialize
和你的例子一样-dematerialize
。
在给出的示例中,登录,可能不需要完成对其进行建模。相反,登录信号可以被定义为行为二元:它要么发送@YES
(例如),要么发送错误。在这些条件下,代码将是:
[[self.viewModel.loginCommand.executionSignals concat] subscribeNext:^(id _) {
// Handle successful login
}];
[self.viewModel.loginCommand.errors subscribeNext:^(NSError *error) {
// Handle failed login
}];
这显然与RAC中典型的subscribeNext:error:completed:
模式有点不同。这仅仅归功于RACCommand
的API。
请注意,-concat
运算符已应用于executionSignals
,以便显示内部值并避免内部订阅。您可能还会在其他-flatten
示例中看到-switchToLatest
或RACCommand
,但只要命令将其allowsConcurrentExecution
属性设置为NO
(这是默认值)然后执行连续发生,使-concat
运算符自然匹配并表达那些串行语义。应用-flatten
或-switchToLatest
实际上可行,因为它们在应用于串行信号信号时会退化为-concat
,但它们会向不适用的读者表达语义。