Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RACSignal for an NSArray of objects

I have an NSArray of ViewModel objects on my ViewController:

@property (nonatomic, strong) NSArray *viewModels;

A ViewModel object looks something like this:

@interface ViewModel : NSObject

@property (nonatomic) BOOL isSelected;

@end

I am trying to create a RACSignal for the enabledSignal on a RACCommand's init method:

- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock

This signal will tell the Command to be enabled if there are either 0 viewModel objects selected or if the number of viewModels selected is equal to the total count of the viewModels.

I can create a RACSequence which will give me the viewModel objects that are selected by this code:

RACSequence *selectedViewModels = [[self.viewModels.rac_sequence

                                        filter:^BOOL(ViewModel *viewModel) {
                                            return viewModel.isSelected == YES;
                                        }]

                                       map:^id(ViewModel *viewModel) {
                                           return viewModel;
                                       }];

How would I go about creating the valid signal?

like image 550
strickland Avatar asked Mar 21 '23 20:03

strickland


1 Answers

To observe all of the latest view models (and only the latest view models) for changes, we'll need to set up new KVO observations each time the array changes.

The most natural way to represent this is with a signal of signals. Each "inner" signal represents a set of observations on one version of viewModels, and then we'll use -switchToLatest to ensure that only the newest signal takes effect:

@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 `isSelected` property for changes.
                return RACObserve(viewModel, isSelected);
            }]
            // 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];
like image 109
Justin Spahr-Summers Avatar answered Apr 15 '23 12:04

Justin Spahr-Summers