Search code examples
macoscocoacore-datacocoa-bindingsnsarraycontroller

Observing NSArrayController changes: detect insertion or deletion


I have an NSArrayController that is bound to an entity "Address" in my Core Data model. I want to monitor which new objects are inserted into this Address entity, or which existing objects are deleted. I'm trying to do this through KVO, but I'm not sure how I should go about this.

In my awakeFromNib, I add the view controller class as the observer for "arrangedObjects":

[self.addressArrayController addObserver:self
                                    forKeyPath:@"arrangedObjects"
                                       options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
                                       context:@"Address"];

and I do get the observer notifications in this:

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

   if( object == self.addressArrayController) {
      // what do do here?
   }
}

The problem is that the change dictionary is always nil (due to some long-standing Apple bug, I believe), so I have no idea which object was added or deleted, or even something was added or deleted! And I do need the exact object that was added or deleted, ideally without having to traverse all the objects for this entity and trying to figure out based on the object's timestamp or any other criterion.

What is the best way to do this?


Solution

  • Adapted from my answer here, which lays out the code for what gaige suggests in the comments.

    Register for NSManagedObjectContextObjectsDidChangeNotification:

    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(mocDidChangeNotification:)
                                  name:NSManagedObjectContextObjectsDidChangeNotification
                                               object: nil];
    

    And parse the userInfo dictionary in the corresponding method:

    - (void)mocDidChangeNotification:(NSNotification *)notification
    {
        NSManagedObjectContext* savedContext = [notification object];
    
        // Ignore change notifications for anything but the mainQueue MOC
        if (savedContext != self.managedObjectContext) {
            return;
        }
    
        // Ignore updates -- lots of noise from maintaining user-irrelevant data
    
        // Log insertion
        for (NSManagedObject* insertedObject in 
               [notification.userInfo valueForKeyPath:NSInsertedObjectsKey])
        {
            if ([insertedObject isKindOfClass:[Address class]] )
            {
                NSLog(@"Inserted Address");
            }
        }   
    
        // Log deletion
        for (NSManagedObject* deletedObject in 
               [notification.userInfo valueForKeyPath:NSDeletedObjectsKey])
        {
            if ([deletedObject isKindOfClass:[Address class]] )
            {
                NSLog(@"Deleted Address");
            }
        }
    
    }