objective-cformsdynamicreactive-cocoaracsignal

How to set up a pipeline for dynamically created RACSignals - dynamic Form application - Reactive Cocoa


I'm working on a Form which consists of dynamically created Fields, and I'd like each Field to have a method that returns a RACSignal which sends a nextEvent each time the Field's value changes.

The payload of the nextEvent should include BOOL that indicates weather the Field is complete (which wraps up some validation logic along the way).

The best I can illustrate my intent so far is with the pseudo-code below:

// Inside Form.m
// Create an array of signals from all the fields in the form 

@property(nonatomic) BOOL *formComplete;

-(void)setUpSignals {

    NSMutableArray *fieldCompleteSignals = [NSMutableArray arrayWithCapacity:0]; 
    for (Field *field in self.fields)
    {
        RACSignal *completeSignal = [field complete]; 
        [fieldCompleteSignals addObject:completeSignal];
    }

    // ? S.O. Help here
    [RACSignal combineLatest:self.fieldCompleteSignals reduce:^id {

        self.formComplete = ?? // combined BOOL values, if any are NO, then returns NO- if all are YES, returns YES
        return ??; // when all the BOOL values returned from the fields are YES, then the form is complete
    }];


}

// Inside Field.m
@property(nonatomic, copy) NSString *value;

-(RACSignal *)fieldComplete
{
    return  [RACObserve(self, value) map:^id(id value) {
        return @([self validateCurrentValue]);
    }];
}


-(BOOL)validateCurrentValue
{
     // snazzy validation logic here 
    return YES | NO;
}

This Form is dynamically created from an XML document, and I'd like to set up a pipeline that handles dynamically created RACSignals.

What I'd like to end up with is a stream of BOOL values, collected from each Field, and reduced to a single formIsComplete BOOL- I'm wondering if I'm on the right track to setting up this pipeline if there's a better way (or other examples) of handling this?


Solution

  • Fo your first question, you can use map along with RACTuple:

    @property (assign, nonatomic) BOOL formComplete;
    
    - (void)setUpSignals {
    
        NSMutableArray *fieldCompleteSignals = 
        [NSMutableArray arrayWithCapacity:self.fields.count];
    
        for (Field *field in self.fields) {
            RACSignal *completeSignal = [field complete]; 
            [fieldCompleteSignals addObject:completeSignal];
        }
    
        RAC(self, formComplete) = 
        [[RACSignal 
          combineLatest:self.fieldCompleteSignals] 
          map:^id (RACTuple *allValues) { 
          return @([allValues.rac_sequence all:^BOOL(id value) {
             return [value boolValue];
         }]);
        }];    
    }
    

    RACTuple is an ordered collection and conforms to NSFastEnumeration protocol (To learn more about RACTuple see RACTuple reference).

    For your second question, yes boxing primitive types is appropriate. Say that you would like to enable some button only when some other BOOL is YES, you can do it like this:

    RAC(self.button, enabled) = RACObserve(self, formComplete);