Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge Two RACSignals, Complete on First Completion

I have two RACSignals one of which is a timer and the other one indicates there's work to do. Since the work indication is sometimes unreliable, there's the timer, which takes care that work will be done periodically if the precise notifications don't work.

The general setup is:

    RACSignal *signal = [RACSignal merge:@[
                         [[RACSignal interval:0.5 onScheduler:[RACScheduler scheduler]]
                           filter:^BOOL(__unused id _x) {
                               return isThereAProblemInDeliveringWork();
                           }],
                         incomingWorkSubject
                         ]];

Now I want signal to complete as soon as incomingWorkSubject was completed. So, in general: Complete the merged signal as soon as any of its input signals completed. Unfortunately, [RACSignal merge:...] doesn't do what I want and apparently waits for all incoming signals to complete.

What would be the ReactiveCocoa-way of achieving that?

like image 283
Johannes Weiss Avatar asked Sep 18 '13 15:09

Johannes Weiss


1 Answers

The most obvious approach is to apply -takeUntil:, which completes a signal when a supplied signal either sends a value, or completes. Since you only want completion when incomingWorkSubject completes, and not when it sends a value, use -ignoreValues.

I've extracted the timer signal to a variable for readability.

RACSignal *timer = [[RACSignal
    interval:0.5 onScheduler:[RACScheduler scheduler]]
    filter:^BOOL(__unused id _x) {
        return isThereAProblemInDeliveringWork();
    }];

RACSignal *signal = [[RACSignal
    merge:@[ incomingWorkSubject, timer ]]
    takeUntil:[incomingWorkSubject ignoreValues]];

For the sake of discussion, there's another way of going about this that might fit the bill. Instead, using the timer defined above, take samples of incomingWorkSubject using -sample:. How you do this depends on the nature of incomingWorkSubject. If it's a RACReplaySubject, and you know a value will be available, it can be sampled directly.

RACSignal *signal = [RACSignal merge:@[
    incomingWorkSubject,
    [incomingWorkSubject sample:timer]
]];

However, if incomingWorkSubject is a vanilla RACSubject, then you'll need to compose a new signal to include an initial value, with -startWith:, so that -sample: has a value to send when timer fires.

RACSignal *signal = [RACSignal merge:@[
    incomingWorkSubject,
    [[incomingWorkSubject startWith:initialValue] sample:timer]
]];

In english, this creates a signal that sends the latest value from incomingWorkSubject, and also will send a recent cached value from incomingWorkSubject, but only when a periodic check to isThereAProblemInDeliveringWork() is affirmative. The merged signal completes when incomingWorkSubject completes.

like image 86
Dave Lee Avatar answered Oct 14 '22 20:10

Dave Lee