Ios 如何以适当的方式将信号与反应可可联系起来?
我正在新的iOS应用程序中使用ReactiveCocoa。我对反应式编程还不熟悉,所以我仍在努力理解传递信号的正确方式。 现在我有了“使用Twitter登录”按钮的以下流程Ios 如何以适当的方式将信号与反应可可联系起来?,ios,objective-c,reactive-programming,reactive-cocoa,Ios,Objective C,Reactive Programming,Reactive Cocoa,我正在新的iOS应用程序中使用ReactiveCocoa。我对反应式编程还不熟悉,所以我仍在努力理解传递信号的正确方式。 现在我有了“使用Twitter登录”按钮的以下流程 ALTUserManager类通过调用呈现Twitter登录面板的库中的一些函数来管理整个登录阶段,并执行所有OAuth操作: - (RACSignal *)loginTwitter:(UIViewController *)vc { RACSignal *loginSignal = [RACSignal create
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;
}
在我的视图控制器中,我正在处理显示逻辑,在显示进度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 subscribebecompleted]
行。正确的方法是什么?
谢谢
更新
我尝试将所有逻辑移到RACCommand
中的ViewModel,但仍然需要捕获RACCommand
中发生的错误。
订阅errors
信号不是一个选项,因为RACCommand
仍然会返回completed
事件,从而使我的演示逻辑无法判断一切是否正常。
我还没有尝试在rac命令中设置BOOL,在出现错误时会产生副作用,并在视图中观察它。但这种方法似乎有点老套。不确定您是否看过设计指南,但这些指南向您展示了一些如何避免
-subscribeNext:error:completed:
模式的解决方案。具体而言:
- RAC()或RACChannelTo()宏可用于将信号绑定到属性,而不是在发生更改时执行手动更新
- -rac_liftSelector:withSignals:方法可用于在一个或多个信号触发时自动调用选择器
- 运算符-takeUntil:可用于在事件发生时自动处理订阅(如在UI中按下“取消”按钮)
您可以使用
then
helper稍微简化嵌套,这将简化错误处理并防止单独的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
信号完成之前再次触发,您可能会进入奇怪的状态。考虑到应用程序的其余部分,这可能是不可能的,但如果孤立地看这段代码,我会很担心
更好的做法可能是将然后块移动到RACCommand
中,以确保这种情况永远不会发生(因为RACCommand
在前一个命令执行完毕之前不会再次执行)。尽管在没有看到更多代码的情况下,我无法确定这是否是一个合理的更改
这是一件需要进一步清理的棘手事情,因为它本身就有副作用。如果你为ALTAlert
类创建一个反应式桥接器,你可以清理很多订阅,就像你可以说的“看看这个信号,让你的状态反映出来。”然后你就可以传递执行信号,而不必担心在这里做更糟糕的事情
那么你唯一真正的副作用就是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];
}];
我查过了,但看起来不像我的案子。该命令绑定到一个按钮,该按钮通过RACCommand
启动Twitter登录。该命令作为下一个信号发送。在这一点上,我应该plattmap
或subscribeNext
到返回的信号,这不是很优雅。有没有一种方法可以告诉RACCommand将其内部信号发送给我?为什么您认为flattmap
不是很优雅?据我所知,这是一种相当标准的信号链接方式。我并不是说平面地图不雅观。事实上,我需要在不优雅的内在信号上筑巢。我检查了你提到的复制品,它解决了我的问题,但它看起来非常复杂。我认为可能有一个方法或宏来解开这个用例。我同意它看起来很复杂。我认为这是由于RACCommand
本身的复杂性造成的。这是一个非常强大的概念,但在这种情况下,强大的力量带来了巨大的复杂性。有人计划在ReactiveCocoa 3.0中弃用RACCommand
:可能的重复我会在第一步中将然后块移动到RACCommand
,但所有对ALTAlert
的调用实际上只是显示或取消的进度HUD,它们属于视图。我可能只是绑定一个属性,以便显示/隐藏ALTAlert
。我可能最终会将ALTAlert错误
绑定到整个RAC命令的副作用。我要试试看。我试过走那条路线,但我仍然需要从RACCommand
获取错误。问题是我仍然需要subscribeNext
到errors
,这很好,但我还将从执行信号中接收完成的。我想我会坚持使用当前的实现,直到RACCommand
的另一个实现可用为止。
[[[self.viewModel.twitterLoginCommand.executionSignals flattenMap:^(id x) {
return [x materialize];
}] filter:^(RACEvent *event) {
return event.eventType == RACEventTypeCompleted;
}] subscribeNext:^(id x) {
@strongify(self);
[self goToChart];
}];