I'm using ReactiveCocoa in a new iOS app. I'm new to reactive programming so I'm still trying to understand what's the proper way to chain signals. Right now I have the following flow for the "login with Twitter" button.
The ALTUserManager
class has the following method for managing the whole login phase by calling some functions in a library that presents the Twitter login panel and does all of the OAuth stuff:
- (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;
}
I'm using the MVVM pattern so in my ViewModel I've added the following command inside its init method:
self.twitterLoginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [[ALTUserManager sharedInstance] loginTwitter:nil];
}];
In my view controller I'm handling the presentation logic where I block the interface while showing a progress hud and eventually report the error or go past the login screen if everything is fine:
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];
}];
I'm pretty sure this could be rewritten in a better way. My concern is mainly about that [x subscribeCompleted]
line. What would be the correct approach?
Thanks!
UPDATE
I tried moving all the logic to the ViewModel inside the RACCommand
but I still need to catch the errors happening inside the RACCommand
.
Subscribing to the errors
signal isn't an option as the RACCommand
would still return the completed
event as well thus making my presentation logic unable to tell if everything went fine or not.
I haven't tried setting a BOOL inside the RACCommand with a side-effect in case of errors and observe it in the view. But that approach seems a bit hacky anyway.