Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactiveCocoa example with NSMutableArray push/pop?

Could someone provide a one-line example of using ReactiveCocoa abstractions to achieve something like this:

// pseudo-code
NSMutableArray *array = @[[] mutableCopy];
RACSignal *newValue = RACAbleWithStart(array); // get whole array or maybe just added/removed element on push/pop

[newValue subscribeNext:^(NSArray *x) {
  // x is whole array
}]

[newValue subscribeNext:^(id x) {
  // x is new value
}]

[newValue subscribeNext:^(id x) {
  // x is removed value
}]

I see that some extensions for NSArray were removed in favor of Mantle https://github.com/ReactiveCocoa/ReactiveCocoa/pull/130 But still can't find simple example of NSArray manipulation.

like image 1000
Nik Avatar asked Sep 13 '13 12:09

Nik


2 Answers

You can't observe an array for changes. ReactiveCocoa uses key-value observation. Which, as the name suggests, only observes changes to keyed attributes (dictionary members, properties, etc.).

What you can do is observe an array property for changes:

@interface Blah : NSObject
@property (copy, readonly) NSArray *arrayProperty;
@end

// later...
Blah *blah = [Blah new];
[RACObserve(blah, arrayProperty) subscribeNext:^(NSArray *wholeArray){}];

If you want to know which objects where inserted/removed then you have two options. You could work it out by storing each array and comparing each with the previous. This is simplest but will perform badly with very large arrays. AFAIK, ReactiveCocoa does not have built-in operations to do this.

Or you could implement KVO collection accessors and ensure that changes to the array are made using mutableArrayValueForKey:. This avoids creating a new array whenever any changes are made, and also notifies observers of changes made to the proxy array returned by mutableArrayValueForKey:.

Observing change info with ReactiveCocoa is slightly more involved:

RACSignal *changeSignal = [blah rac_valuesAndChangesForKeyPath:@keypath(blah, arrayProperty) options: NSKeyValueObservingOptionNew| NSKeyValueObservingOptionOld observer:nil];
[changeSignal subscribeNext:^(RACTuple *x){
    NSArray *wholeArray = x.first;
    NSDictionary *changeDictionary = x.second;
}];

The change dictionary tells you what kind of change was made to the array, which objects were inserted/removed, and the indexes of the inserted/removed objects.

It is documented at: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSKeyValueObserving_Protocol/Reference/Reference.html

like image 135
Chris Devereux Avatar answered Oct 22 '22 00:10

Chris Devereux


The swift equivalent for Chris' solution:

let signal = self.object.rac_valuesAndChangesForKeyPath("property", options:      NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Old, observer:self.object)
signal.subscribeNext { (object) -> Void in
   if let tuple = object as? RACTuple {
        var wholeArray = tuple.first as? NSArray
        var changeDictionary = tuple.second as? NSDictionary
    }
}

Also make sure you change your contents property in KVO compliant ways.

// This is wrong and wont send values to RAC signals
[self.contents addObject:object];

// This is correct and will send values to RAC signals
NSMutableArray *contents = [account mutableArrayValueForKey:@keypath(self, contents)];
[contents addObject:object];

Edit: To make things more clear, put the name of your array in place of property. For example:

lazy var widgets:NSMutableArray = NSMutableArray()
let signal = self.rac_valuesAndChangesForKeyPath("widgets", options:      NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Old, observer:self)
like image 23
Antoine Avatar answered Oct 22 '22 00:10

Antoine