Search code examples
reactive-cocoa

RAC: how to subscribe to signal lazily i.e. do not trigger its execution if that signal already was or going to be executed by other subscriber?


This is question about RAC 2.0 which I am very new to:

Let's say I have a signal:

- (RACSignal *)signalWithAsyncWorkInIt;

When my app is starting I want to do

RACSignal *sig1 = [self.signalWithAsyncWorkInIt subscribeNext:...];

// then somewhere else later:
RACSignal *sig2 = [self.signalWithAsyncWorkInIt subscribeNext:...]; // looking for some option other than subscribeNext:

but in a way that sig2's subscribeNext: method would not trigger execution of signalWithAsyncWorkInIt if sig1 already did or is just going to do it i.e. so that sig2 would just replay the "latest result" of signalWithAsyncWorkInIt in case if it exists and wait for something else like sig1 to trigger signalWithAsyncWorkInIt the first time it if it has not been triggered yet.


Solution

  • I'm not sure if I understood correctly but maybe what you are looking for is replay, replayLast or replayLazily.

    With replay you can avoid multiple execution of a signal. It multicasts the result to several subscribers without executing the operation for every subscription.

    Simple example:

    RACSignal* asyncOperationSignal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"execute something here and return with the result");
        [subscriber sendNext:@"42"];
        return nil;
    }]
    replayLazily]; // call replayLazily to make sure the operation only executes after the first subscription
                   // and returns the result for every consecutive subscription
    
    [asyncOperationSignal subscribeNext:^(NSString* result) {
        NSLog(@"Result: %@", result);
    }];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // subscribe again later in the code
        [asyncOperationSignal subscribeNext:^(NSString* result) {
            NSLog(@"Result: %@", result);
        }];
    });
    

    The output will be:

    execute something here and return with the result
    42
    42
    

    Here is a really well written article about this topic: http://spin.atomicobject.com/2014/06/29/replay-replaylast-replaylazily/