Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/sharepoint/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 如何以适当的方式将信号与反应可可联系起来?_Ios_Objective C_Reactive Programming_Reactive Cocoa - Fatal编程技术网

Ios 如何以适当的方式将信号与反应可可联系起来?

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

我正在新的iOS应用程序中使用ReactiveCocoa。我对反应式编程还不熟悉,所以我仍在努力理解传递信号的正确方式。 现在我有了“使用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;
}
在我的视图控制器中,我正在处理显示逻辑,在显示进度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];
}];