Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to setup KVO on a Collection (NSArray or NSSet) of NSManagedObjects

I have an iPad app that has a UITableViewController that implements the NSFetchedResultsControllerDelegate. (Mostly using the CoreDataTableViewController code from the Stanford iOS classes.)

I have a secondary model object (self.locations) that is an array of Location objects which is a subclass of NSManagedObjects. This array drives the content of a UISegmentedControl that filters my main fetchedResultsContoller.

It is possible to modify the contents of self.locations via a popover. I want to set up some sort of observing so that my main UITableViewController can watch for changes in the objects stored in self.locations and reload the UISegmentedControl if necessary.

This might also result in a reload of the main data in the table, so I want to be careful to not reload on every little modification.

I think I understand how to setup KVO on a single NSManagedObject but I'm not sure how to do it on object contained in an array. I understand that I can use another NSFetchedResultsController, but my self.locations object does not drive a second UITableView, so I'm not sure it makes sense.

like image 730
DeepFriedTwinkie Avatar asked Mar 20 '12 22:03

DeepFriedTwinkie


1 Answers

It is pretty straight-forward to observe a one-to-many relationship if all you want to know if objects are added, removed, replaced, or re-ordered. In fact, it is done exactly the same way as with a normal object:

[self addObserver:self
       forKeyPath:@"locations"
          options:0
          context:NULL];

Then implement the following to receive notifications (partially copied from Apple docs):

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {

    if ([keyPath isEqual:@"locations"]) {
        // Your custom code here.
    }

    // Be sure to call the superclass's implementation *if it implements it*.
    [super observeValueForKeyPath:keyPath
                         ofObject:object
                           change:change
                           context:context];
}

Don't forget to stop observing at some point:

[self removeObserver:self forKeyPath:@"locations"];

And, although you didn't ask, if you want to know if any of the objects contained in the relationship have changed (and not just the NSSet that you are watching here) then you have to observe the individual objects.

EDIT

Per your comment, you do want to observe the individual objects. This is fairly straight-forward for "normal" objects, but a managed object takes a bit more work because you have to observe the individual keys in the object, which would look something like this:

- (void)observeManagedObject:(NSManagedObject *)myObject {
    NSArray *myObjectKeys = [[[myObject entity] attributesByName] allKeys];
    for (NSString *key in myObjectKeys) {
        [myObject addObserver:self 
                    forKeyPath:key 
                       options:0 
                       context:nil];
    }
}

And then you observe all of the NSManagedObjects in the array like this:

for (NSManagedObject *object in myArray) {
    [self observeManagedObject:object];
}

Do the reverse to stop observing the keys on the managed object!

like image 99
lnafziger Avatar answered Nov 15 '22 00:11

lnafziger