0

I wonder if it's possible to accumulate results of signals happening between nexts of a particular signal. Similar to

[[[RACSignal
    combineLatest:@[signal1, signal2, signal3]]
    takeUntil:signal4]
    subscribeNext:^(id x) {
        // ...
    }];

But not completing after the first signal4's next.

The scenario I'm working with is I have a viewController1 that presents viewController2. viewController2 allows user to change an object that affects data presented in viewController1. Currently I'm exposing an objectChangedSignal on a viewController2 and listening to it from viewController1.

However, I'd like to receive changes of the object directly from the viewController1 but only after it fires viewWillAppear signal and only if changes happened after viewWillDisappear of viewController1.

Hope it makes sense, thank you.

Sash Zats
  • 5,376
  • 2
  • 28
  • 42

1 Answers1

2

One way to buffer values is to use -[RACSignal collect], which collects them into an array until the signal completes:

[[[RACSignal combineLatest:@[ s1, s2, s3 ]]
    collect]
    takeUntil:s4]

Howevever, you probably wanted each value to be sent individually on the final signal, not an array of values. You can use -[RACSignal flattenMap:] to flatten those values back into the final signal:

[[[[RACSignal combineLatest:@[ s1, s2, s3 ]]
    collect]
    takeUntil:s4]
    flattenMap:^(NSArray *collected) {

        return [[collected rac_sequence] signal];

    }]

And since you don't want your signal to be completed after s4, you can use -repeat to resubscribe after every time s4 sends a value. If you didn't do this, the -takeUntil: would cause your signal to complete when that happens.

[[[[[RACSignal combineLatest:@[ s1, s2, s3 ]]
    collect]
    takeUntil:s4]
    flattenMap:^(NSArray *collected) {

        return [[collected rac_sequence] signal];

    }]
    repeat]

Note that if any of the combined signals are cold signals, the resubscription will cause their side effects to occur additional times. So you may have to convert them into a hot signal by multicasting them (or the combined signal):

RACMulticastConnection *mc = [[RACSignal combineLatest:@[ s1, s2, s3 ]] publish];
RACDisposable *d = [mc connect];
[[[mc.signal
    takeUntil:s4]
    flattenMap:^(NSArray *collected) {

        return [[collected rac_sequence] signal];

    }]
    repeat]

You may need to explicitly unsubscribe (by disposing d) if your signal would otherwise live infinitely.

(Nota bene, this code is untested.)

erikprice
  • 6,240
  • 3
  • 30
  • 40
  • Thank you for the extensive answer! But it doesn't really work in a way I imagined; most likely due to the poor description of the task: `s1`, `s2` and `s3` won't necessarily fire. But if they do, I want to be notified only after `s4` fired. – Sash Zats Feb 25 '14 at 13:23
  • If any of `s1`, `s2`, or `s3` doesn't send a value, then `-combineLatest:` will never send a value – it waits until each of the signals given to it sends a value before sending the tuple of values. (There are ways of dealing with this, such as using `-startWith:`.) It also sounds like `-[takeUntil:s4]` is not what you want, since this *completes* the signal when `s4` fires. Perhaps you want one of `-skipUntilBlock:`, `-skipWhileBlock:`, or `-if:then:else:` operations? – erikprice Feb 25 '14 at 15:23