Question

I am using the typical pattern of a cached-replay signal:

- (RACSignal *)resultSignal {
    if (!_resultSignal) {
        _resultSignal = [self createResultSignal];
    }

    return _resultSignal;
}

- (RACSignal *)createResultSignal {
    return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [someObject doWorkWithCompletionBlock:^(id result) {
            [subscriber sendNext:result];
            [subscriber sendCompleted];
        }
                                failure:^(NSError *error) {
            [subscriber sendError:error];
        }];

        return nil;
    }] replay];
}

This works great, but now I want to add the ability to retry if the work failed. I can use -[RACSignal retry] or -[RACSignal retry:], but this would not notify the subscribers of an error immediately. Instead, what I want is for existing subscribers to this signal to get the error, but subsequent calls to -resultSignal to receive a new signal, or retry the existing one.

I could -catch: this signal and set _resultSignal to nil in the block, but then I'd have to worry about race conditions, and I don't think that'd be the "reactive" way to do this.

What would be the appropriate way of implementing this behavior?

Update:

Thanks to @joshaber! I ended up going with my suggested approach, and I think it's not too bad:

- (RACSignal *)resultSignal {
    @synchronized(_resultSignal) {
        if (!_resultSignal) {
            @weakify(self);

            _resultSignal = [[self createResultSignal] catch:^RACSignal *(NSError *error) {
                @strongify(self);

                self.resultSignal = nil;
                return [RACSignal error:error];
            }];
        }

        return _resultSignal;
    }
}

- (void)setResultSignal:(RACSignal *)signal {
    @synchronized(_resultSignal) {
        _resultSignal = signal;
    }
}
Was it helpful?

Solution

I've been thinking about this for a while. Does it help if I wish I had a great answer? ;)

I could -catch: this signal and set _resultSignal to nil in the block, but then I'd have to worry about race conditions, and I don't think that'd be the "reactive" way to do this.

This is what I'd lean towards.

Alternatively, you could use a signal of signals. It'd send whatever the latest signal is. Existing subscribers would be subscribed to the old signal and new subscribers would get the retried signal. But that'd probably require using a RACSubject manually, so I don't know that the additional complexity is worth it, compared to the other solutions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top