Observe every item in a RACSequence, update observations when sequence has new items

104 views Asked by At

This is a similar question to an earlier question that asked about observing every item in a RACSequence — the correct answer was something like:

RACSignal *valid = [[RACSignal combineLatest:
                     [self.viewModels map:^id(ViewModel *viewModel) {
                       return RACObserve(viewModel, state);
                     }]
                    ]
                    map:^(RACTuple *states) {
                      return @([states.rac_sequence all:^BOOL(NSNumber *state) {
                        return state.unsignedIntegerValue == Completed;
                      }]);
                    }
                   ];

My variation on this is that I'd like to also handle the case where ViewModel instances are added/removed from the sequence as well. I can do this by invalidating a RACDisposable stored in an instance variable or property, but it would be great to do this without adding any extra state. What's the right way to do this?

1

There are 1 answers

0
Tony Arnold On

I found the answer in an older post by @justin-spahr-summers: https://stackoverflow.com/a/19711002/63580

Here is the version specific for my question above for posterity:

@weakify(self);

RACSignal *enabled = [[RACObserve(self, viewModels)
    // Map _each_ array of view models to a signal determining whether the command
    // should be enabled.
    map:^(NSArray *viewModels) {
        RACSequence *selectionSignals = [[viewModels.rac_sequence
            map:^(ViewModel *viewModel) {
                // RACObserve() implicitly retains `self`, so we need to avoid
                // a retain cycle.
                @strongify(self);

                // Observe each view model's `state` property for changes.
                return RACObserve(viewModel, state);
            }]
            // Ensure we always have one YES for the -and below.
            startWith:[RACSignal return:@YES]];

        // Sends YES whenever all of the view models are selected, NO otherwise.
        return [[RACSignal
            combineLatest:selectionSignals]
            and];
    }]
    // Then, ensure that we only subscribe to the _latest_ signal returned from
    // the block above (i.e., the observations from the latest `viewModels`).
    switchToLatest];