I\'m using ReactiveCocoa signals to represent calls to RESTful backend in our system. Each RESTful invocation should receive a token as one of the parameters. The token itse
First of all, subscriptions and subjects should generally be avoided as much as possible. Nested subscriptions, in particular, are quite an anti-pattern—usually there are signal operators that can replace them.
In this case, we need to take advantage of the fact that signals can represent deferred work, and create only one signal to perform the actual request:
// This was originally the `first` signal.
RACSignal *apiCall = [RACSignal defer:^{
return [self apiCall:base params:p get:get];
}];
The use of +defer:
here ensures that no work will begin until subscription. An important corollary is that the work can be repeated by subscribing multiple times.
For example, if we catch an error, we can try fetching a token, then return the same deferred signal to indicate that it should be attempted again:
return [[apiCall
catch:^(NSError *error) {
// If an error occurs, try requesting a token.
return [[self
action:@"logon" email:session.user.email password:session.user.password]
flattenMap:^(NSDictionary *json) {
NSString *token = json[@"token"];
p[@"token"] = token;
session.token = token;
// Now that we have a token, try the original API call again.
return apiCall;
}];
}]
replay];
The use of -replay
replaces the RACReplaySubject
that was there before, and makes the request start immediately; however, it could also be -replayLazily
or even eliminated completely (to redo the call once per subscription).
That's it! It's important to point out that no explicit subscription was needed just to set up the work that will be performed. Subscription should generally only occur at the "leaves" of the program—where the caller actually requests that work be performed.