The Reason NSFetchedResultsChangeUpdate Is so Buggy

Always wondered why NSFetchedResultsChangeUpdate is so buggy? The reason is Apple don’t actually use it in their own apps. Instead, what they do is have a UITableViewCell subclass with a custom property setter for the object where they add KVO for only the properties they are interested in updates to, e.g. ones that require updating the cell’s views. This is a pretty good optimisation if you have a complicated object that has lots of non-user properties being updated all the time, e.g. that have server info for remote objects. They don’t go as far as updating each label for each key change, they just update all the views when any of them change. They do some times use `NSObject`’s `cancelPerformSelector` to batch the updates into one UI update. And they might also cache some data, e.g. a count of relational data. They also can use KVO contexts in really interesting ways, like using the context to “group” key paths that all have the same outcome, which saves checking the key is say in a certain array of keys that require view updates.

It makes me so sad to see so many people struggle with `NSFetchedResultsChangeUpdate` and `NSFetchedResultsChangeMove` I wish more people knew to instead go the KVO route. I think it would be cool if when creating an Xcode project they had varying levels of templates. So this KVO usage could perhaps be in an advanced core data app template and the original in a novice one, along with a health warning about not using it if you have table sections or have objects with many non-user visible properties.

I put an example in this sample code. You can see in the example controller there is no longer `NSFetchedResultsChangeUpdate` being used, and also note the workaround for UITableView moveRows not working properly across sections, also something Apple do.

New Way to Override NSManagedObject Properties

There is a new, yet not very well-known way to override NSManagedObject properties without needing to manually call KVO methodsĀ willChangeValueForKey etc. This is achieved using dynamic accessors prefixed with managedObjectOriginal as follows:

Department.h

@interface Department : NSManagedObject
@property (nullable, nonatomic, copy) NSString *name;
@end

Department.m

@interface Department (DynamicAccessors)
- (NSString *)managedObjectOriginal_name;
- (void)managedObjectOriginal_setName:(NSString *)newName;
@end

@implementation Department

@dynamic name;

- (NSString *)name
{
    // invoke the dynamic implementation of name
    NSString *name = [self managedObjectOriginal_name];
    // your custom code
    return name;
}

- (void)setName:(NSString *)name
{
    // invoke the dynamic implementation of setName
    [self managedObjectOriginal_setName:(NSString *)name;
    // your custom code
}

@end

As seen at bottom of What’s New in Core Data in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0.