Dynamic OneToMany ReactiveCocoa binding

66 Views Asked by At

I have something similar to the following structure in my code:

// Model
@interface Child : NSObject
@property (nonatomic, assign) CGPoint position;
@end

@interface Father : NSObject
@property (nonatomic, strong) NSArray <Child *> *children; // (0..n)
@end

// ViewModel
@interface FatherViewModel : NSObject
@property (nonatomic, strong) Father *father;
@property (nonatomic, assign) CGPoint averagePositionOfChildren;
@end

During execution, number of children in each Father member can change (in this case I recreate the whole NSArray) and position of each Child can also change.

Does any elegant solution in ReactiveCocoa exist to map the dynamic number of children positions in the model to averagePositionOfChildren in FatherViewModel?

2

There are 2 best solutions below

0
On BEST ANSWER

Yes, I can see you choose two strategies:

1. Use properties

Use a MutableProperty for children and then create a mapped property for averagePositionOfChildren.

2. Use KVO

Alternatively, you can use KVO to watch changes in children. I.e., you can create a DynamicProperty.

Note that both scenarios would force you to recreate the whole array, as you already noted.

0
On

If anyone is interested, here follows my own solution:

@interface FatherViewModel()
@property (nonatomic, strong) RACDisposable *averageBindingDisposable;
@end

@implementation FatherViewModel
- (instanceType) init {
self = [super init];
...
...
RACSignal *signalOfSignals = [RACObserve(self, father.children)
                         map:^RACSignal *(NSArray <Child *> *setOfChildren) {
                             NSArray <RACSignal *> *arrayOfSignal = [[[setOfChildren rac_sequence]
                                                                      map:^RACSignal *(Child *child) {
                                                                          return RACObserve(child, position);
                                                                      }]
                                                                     array];
                             return [RACSignal combineLatest:arrayOfSignal];
                         }];

[self.averageBindingDisposable dispose];
self.averageBindingDisposable = [[[signalOfSignals flatten]
                  map:^NSValue *(RACTuple *tuple) {
                      NSArray <NSValue *> *arrayOfWrappedCGPoints = tuple.allObjects;
                      CGPoint avg = CGPointZero;
                      // Compute here the average of the points in the array
                      return [NSValue valueWithPoint: avg];
                  }]
                  setKeyPath:@"averagePositionOfChildren" onObject: self];
}];

return self;
}
...
@end