Search code examples
objective-creactive-cocoa

How to directly subscribe a signal of signal without flattenMap when using ReactiveCocoa?


I have a method that returns a signal of signal and I have to use flattenMap to extract the value wrapped in the signal. After that only can I use subscribeNext:complete: to get the value.

The code looks like the follows:

- (RACSignal *)publicSignal {
    return [[self privateSignal] map:^id(NSString *code) {
       return [self chainingSignalUsingCode:code];
    }];
};


[[[obj publicSignal] flattenMap:^id(id value) {
    return value;
}] subscribeNext:^(NSString *someValue) {
    // extract the value;
} completed:^{
    // something to do..
}];

Is there any way to eliminate the flattenMap code so that I can directly subscribe to the publicSignal?

Thanks in advance.


Solution

  • Well, you could just do the flattenMap inside publicSignal...

    - (RACSignal *)publicSignal {
        return [[[self privateSignal] map:^id(NSString *code) {
           return [self chainingSignalUsingCode:code];
        }] flattenMap:^(id value) {
            return value;
        }];
    }
    
    [[obj publicSignal] subscribeNext:^(NSString *someValue) {
        // extract the value;
    } completed:^{
        // something to do..
    }];
    

    But then why are we mapping and then immediately flattenMapping? We can just say:

    - (RACSignal *)publicSignal {
        return [[self privateSignal] flattenMap:^(NSString *code) {
           return [self chainingSignalUsingCode:code];
        }];
    }
    
    [[obj publicSignal] subscribeNext: ...
    

    To get the same effect.

    Bear in mind that flattenMap isn't really "extracting the value in the signal." It's extracting all the values from all the signals. When privateSignal sends new signals, publicSignal is going to next for every value that every signal sends -- it's basically going to remember all past signals that privateSignal sends and forward all of their nexts through. If this is what you want, then great, it works. But if you only want to "unwrap" the latest signal, use switchToLatest instead of flattenMap.

    If you don't want to modify publicSignal, and you want it to be a signal of signals, you can use the simpler flatten method and subscribe to that:

    [[[obj publicSignal] flatten] subscribeNext: ...
    

    Since flattenMap without doing anything is equivalent to flatten (in fact flatten is implemented as a flattenMap that just returns its value). Or, if you only want to subscribe to nexts from the latest signal it sends:

    [[[obj publicSignal] switchToLatest] subscribeNext: ...
    

    But as long as it's a signal of signals, you need to do some operation to "unwrap" it. there's no helper flattenAndThenSubscribe method -- which is good. You can get the effect you want through composition of simple primitives.