How to combine two async network calls with ReactiveCocoa

Viewed 3087

I have two network signals that I want to merge, but with some restrictions.

Let us call the network signals A and B. A does use AFNetworking to look up a resource in the cache and return any response for that request immediately. B also considers the cache, but can go to the remote server for revalidation of the response.

Ok, so what I want to do:

Request A:

  • should do sendNext as soon as possible.
  • if B already has done a sendNext, we will just ignore A.
  • if something goes wrong, and A creates an error, we should just ignore it.

Request B:

  • should do sendNext as soon as possible, even if A already has done a sendNext.
  • if something goes wrong, I am interessed in the error from B, but it should not stop A.

My current solution is this:

- (RACSignal *)issueById:(NSString *)issueId {

    RACSignal *filterSignal = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
        RACSignal *cacheSignal = [[IssueWSRequest instance] issueWithId:issueId cachePolicy:NSURLRequestReturnCacheDataDontLoad];

        return [cacheSignal subscribeNext:^(id x) {
            [subscriber sendNext:x];
        } error:^(NSError *error) {
            NSLog(@"Ignore error");
            [subscriber sendCompleted];
        } completed:^{
            [subscriber sendCompleted];
        }];
    }];

    RACSignal *remoteSignal = [[IssueWSRequest instance] issueWithId:issueId cachePolicy:NSURLRequestUseProtocolCachePolicy];

    RACSignal *combined = [RACSignal merge:@[newSign, remoteSignal]];
    return combined;
}

I know that this solution does not fulfill my requirements, so I wonder if anyone could help me with a better solution.

My solution (derived from @JustinSpahr-Summers answer):

- (RACSignal *)issueById:(NSString *)issueId {

    RACSubject *localErrors = [RACSubject subject];

    RACSignal *remoteSignal = [[IssueWSRequest instance] issueWithId:issueId cachePolicy:NSURLRequestUseProtocolCachePolicy];

    RACSignal *cacheSignal = [[[[[[IssueWSRequest instance] issueWithId:issueId cachePolicy:NSURLRequestReturnCacheDataDontLoad] 
            takeUntil:remoteSignal] doError:^(NSError *error) {
                [localErrors sendNext:error];
            }] finally:^{
                // Make sure to complete the subject, since infinite signals are
                // difficult to use.
                [localErrors sendCompleted];
            }]
            replayLazily];

    return [RACSignal merge:@[
            [cacheSignal catchTo:[RACSignal empty]],
            remoteSignal
    ]];
}
1 Answers
Related