Search code examples
cocoacore-datacocoa-bindings

NSOutlineView, NSTreeController and a heterogeneus hierarchy


In page 40 of Marcus Zarra's Core Data book, he suggests that, since NSTreeController requires the same key for accessing all the objects in the hierarchy (for example, children) and that could imply less meaningful relationship names, you can write additional accessors for the desired relationships. I think that this is a great idea, but I am not sure of how to implement it.

Let me use Aperture’s data model as an example: You can have many libraries, each one of them can have many projects, and each of them can have many photos. So, if I name my Entities Library, Project, and Photo and their relationships projects, photos and nothing respectively, is the following a proper implementation for Library?

Library.h

@interface Library: NSManagedObject {
}

@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSSet *projects;
@property (nonatomic, retain) NSSet *children;
@property (nonatomic, retain) id parent;
@end

@interface Library (CoreDataGeneratedAccessors)
- (void)addProjectsObject:(Project *)value;
- (void)removeProjectsObject:(Project *)value;
- (void)addProjects:(NSSet *)value;
- (void)removeProjects:(NSSet *)value;
- (id)parent;
- (void)setParent;
@end

and Library.m

#include "Library.h" 

@implementation Library

@dynamic title;
@dynamic projects;

- (NSSet*) children {
    [self willAccessValueForKey:@"children"];
    NSSet *set = [self valueForKey:@"projects"];
    [self didAccessValueForKey:@"children"];
    return set;
}

- (void) setChildren:(NSSet*)children {
    [self willChangeValueForKey:@"children"];
    [self setValue:children forKey:@"projects"];
    [self didChangeValueForKey:@"children"];
}

- (id)parent {
    [self willAccessValueForKey:@"parent"];
    [self didAccessValueForKey:@"parent"];
    return nil;
}

- (void)setParent:(id)parent {
    // Proposed parent value is ignored. Libraries have no parent.
    [self willChangeValueForKey:@"parent"];
    [self didChangeValueForKey:@"parent"];
}

@end
  1. Should children and parent be properties in the header file?

  2. Is this the suggested implementation? Should I also include addChildrenObject:, removeChildrenObject: , addChildren:, and removeChildren:? And implement them? (Same goes for the parent methods.)

  3. I assume that children doesn’t appear at all in the Core Data model. Is that right? How are the reverse relationships inferred then?

  4. Should I call [self willChangeValueForKey:@"children"] in setChildren: so children is KVO compliant? (Same for the other accessors.)

  5. In page 41 M. Zarra recommends to implement NSOutlineDataSource instead of using NSTreeController due to “results [that] can be unexpected and unclear”. Does anyone know what are those limitations?

  6. Finally, if I implement NSOutlineDataSource, would you recommend caching the fetch for the root objects? And if so, what is the proper way to maintain this cached array in sync with Core Data?

Thank you.

Best regards,

Jorge


Solution

  • The problem I see here is that, while setting the "children" property will trigger KVO for the "projects" property, the reverse is not true. So, if you add a project to the library object via the "projects" relationship, the outline view will not update because it will not see any change in the "children" property.

    The easiest way to enable this is to implement a method like so:

    + (NSSet*)keyPathsForValuesAffectingChildren
    {
        return [NSSet setWithObject:@"projects"];
    }
    

    That should make any changes to the "projects" property also trigger a KVO notification for "children" as well.

    On a side note, since the "children" property isn't part of your Core Data model, I don't think the will/didAccessValueForKey: calls are strictly necessary, though I don't think they'll hurt anything. Also, if you implement the method I mentioned above, you should no longer need to call will/didChangeValueForKey: in the setChildren: method, as Cocoa should automatically trigger the KVO for that when the "projects" key is changed.