In ReactiveCocoa, if we chaining several dependent signals, we must use subscribeNext:
for the next signal in the chain to receive the value previous signal produced (for example, a result of an asynchronous operation). So after a while, the code turns into something like this (unnecessary details are omitted):
RACSignal *buttonClickSignal = [self.logIn rac_signalForControlEvents:UIControlEventTouchUpInside];
[buttonClickSignal subscribeNext:^(UIButton *sender) { // signal from a button click
// prepare data
RACSignal *loginSignal = [self logInWithUsername:username password:password]; // signal from the async network operation
[loginSignal subscribeNext:^void (NSDictionary *json) {
// do stuff with data received from the first network interaction, prepare some new data
RACSignal *playlistFetchSignal = [self fetchPlaylistForToken:token]; // another signal from the async network operation
[playlistFetchSignal subscribeNext:^(NSDictionary *json) {
// do more stuff with the returned data
}];
// etc
}];
}];
This ever-increasing nesting does not look much better than the non-reactive example that is given in the documentation:
[client logInWithSuccess:^{
[client loadCachedMessagesWithSuccess:^(NSArray *messages) {
[client fetchMessagesAfterMessage:messages.lastObject success:^(NSArray *nextMessages) {
NSLog(@"Fetched all messages.");
} failure:^(NSError *error) {
[self presentError:error];
}];
} failure:^(NSError *error) {
[self presentError:error];
}];
} failure:^(NSError *error) {
[self presentError:error];
}];
Am I missing something? Is there a better pattern of chaining dependent work in ReactiveCocoa?
This is when the RACStream and RACSignal operators start really coming in handy. In your particular example, you can use -flattenMap:
to incorporate results into new signals:
[[[buttonClickSignal
flattenMap:^(UIButton *sender) {
// prepare 'username' and 'password'
return [self logInWithUsername:username password:password];
}]
flattenMap:^(NSDictionary *json) {
// prepare 'token'
return [self fetchPlaylistForToken:token];
}]
subscribeNext:^(NSDictionary *json) {
// do stuff with the returned playlist data
}];
If you don't need the results from any step, you can use -sequenceMany:
or -sequenceNext:
instead for a similar effect (but for a clearer expression of intent).