views:

236

answers:

1

When we have an object who has a property that's generated based on other properties, usually we implement the +keyPathsForValuesAffecting{PropertyName} class method.

What I'm trying to do is basically the same thing for a property on my NSManagedObject, but traversing a relationship.

My model is simple; I have two Entities, App and Version (I'm creating an appcast-generating app). When the App's properties are changed, because I implemented the method above, the -appcast string is changed, and all bindings are updated appropriately.

However, when any properties on any of a specific App's Versions (to-many relationship) change, the -appcast property is not generated appropriately. I can haz fix/workaround?

+1  A: 

This is a bit of a late answer, but I think it's a common situation, and the answer definitely isn't readily-apparent.

I generally watch for the managedObjectContext to change and then check if the any of the changed objects are ones that I want to look out for. So, in your NSManagedObject subclass:

// We need to register for the notification in both awakeFromFetch
// AND awakeFromInsert, since either one could be called, depending on
// if the object was previously-created or not
- (void)awakeFromFetch {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(managedObjectContextDidChange:) name: NSManagedObjectContextObjectsDidChangeNotification object:[self managedObjectContext]];
}

- (void)awakeFromInsert {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(managedObjectContextDidChange:) name: NSManagedObjectContextObjectsDidChangeNotification object:[self managedObjectContext]];
}

- (void)managedObjectContextDidChange:(NSNotification *)notification {
    // Get a set containing ALL objects which have been changed
    NSSet *insertedObjects = [[notification userInfo] objectForKey:NSInsertedObjectsKey];
    NSSet *updatedObjects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];
    NSSet *deletedObjects = [[notification userInfo] objectForKey:NSDeletedObjectsKey];

    NSSet *changedObjects = [insertedObjects setByAddingObjectsFromSet:updatedObjects];
    changedObjects = [changedObjects setByAddingObjectsFromSet:deletedObjects];

    if ([changedObjects intersectsSet:[self versions]]) {
        [self willChangeValueForKey:@"appCast"];
        [self didChangeValueForKey:@"appCast"];
    }
}

This is certainly not ideal from a performance perspective, since this notification is going to fire every time anything in your object graph changes, but I've found it to be the most straightforward way to accomplish this.

Matt Ball