Work around for “Auto Rearrange Content” for sorted Core Data NSArrayControllers

The Problem

There seems to be a bug in Apples implementation of NSArrayController when “Auto Rearrange Content” is ticked in Interface Builder. You’ll typically be using this when sorting via a NSSortDescriptors.
I haven’t quite determined when this bug raises it’s head, but it looks to be when your NSTableView is bound to a ValueKeyPath via a relationship. For example: A core data model with two entities “Value” and “Type”, Type and attribute “typeName” and Value has an attribute “name” and a relationship “type” connected to the “Type” entity.

We will need an NSArrayController set to use the entity “Value”. Now, if we create a NSTableView with two columns, one bound to the KeyPath “name” and the other to “type.typeName”. When the application is run, we will start seeing error logged to the console (you may need to start clicking on the NSTableView headers to resort)

Cannot remove an observer for key path....

Also this doesn’t appear to be a problem when building a 64-bit application.

The Solution

I found the work-around for this was to sub-class NSArrayController, and subscribe to Core Data update notifications, on receipt of this check there is an update to one of our entities, and manually call rearrangeContent.

There is no real content to the interface file
ArrayControllerFix.h

#import <Cocoa/Cocoa.h>
 
@interface ArrayControllerFix : NSArrayController {
}
 
@end

…and only a small amount in the implementation file.
ArrayControllerFix .m

- (void)awakeFromNib {
        [super awakeFromNib];
 
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(coreDataEntityDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:[self managedObjectContext]];
}
 
- (void)coreDataEntityDidChange:(NSNotification *)notification {
        if ([self sortDescriptors] &amp;&amp; [[self sortDescriptors] count != 0]) {
                NSArray *insertedEntities = [[[note userInfo] valueForKey:NSInsertedObjectsKey] valueForKeyPath:@"entity.name"];
                NSArray *updatedEntities  = [[[note userInfo] valueForKey:NSUpdatedObjectsKey] valueForKeyPath:@"entity.name"];
                NSArray *deletedEntities  = [[[note userInfo] valueForKey:NSDeletedObjectsKey] valueForKeyPath:@"entity.name"];
 
                static NSString *entityName = [self entityName];
                if ([insertedEntities containsObject:entityName] || [updatedEntities containsObject:entityName] || [deletedEntities containsObject:entityName]) {
                        [super rearrangeObjects];
                }
        }
}