views:

27

answers:

2

My document-based Cocoa application uses a NSOutlineView/NSTreeController combo, bound to the document's Core Data store. My NSTreeController has the fetch predicate isRoot == YES. isRoot is a transient boolean attribute with a default value of NO. My root model's awakeFromInsert calls:

[self setIsRoot:[NSNumber numberWithBool:YES]];

I'm able to add objects to the hierarchy just fine, but when I try to load a document I just saved, I get an exception:

[<NSDictionaryMapNode 0x1001a8190> valueForUndefinedKey:]: this class is not key value coding-compliant for the key isRoot.

I can work around this exception and successfully load a newly-saved document if I change the isRoot attribute to non-transient in the xcdatamodel, but based on my understanding of the transient flag it should not cause a problem, and this really isn't the kind of data that should be persisted.

I have also tried implementing -isRoot in the NSManagedObject subclasses to return the appropriate fixed value, as well as making the same setIsRoot: call within awakeFromFetch, both to no avail.

Is there some other subtlety I'm missing? I can't imagine that fetch predicates don't support transient attributes. I don't know much about the inner workings of Core Data but it seems interesting that it's trying to look up isRoot on the store-specific class and not my NSManagedObject subclass.

A: 

Have you made sure that the NSTreeController is set to control an entity rather than a class?

From your error, it looks like it might be set to a class with the default - NSMutableDictionary.

Tree Controller attribute screenshot

I'd also argue that maybe isRoot could be persisted. It depends on what you're trying to do with your app, of course, but if it's a tree view that gets loaded on app run I'd either make isRoot persist.

John Gallagher
John - Yes, I have set the NSTreeController to entity mode with the appropriate class name filled in. In this particular case I believe that NSDictionaryMapNode relates to how Core Data is persisting the data to disk (I chose Binary when saving the document, if I choose XML the exception is on NSXMLDocumentMapNode).
Adam Preble
+1  A: 

I can't imagine that fetch predicates don't support transient attributes.

After a bit of research, I can tell you that they don't. See this document. Quote:

You cannot fetch using a predicate based on transient properties (although you can use transient properties to filter in memory yourself).

I've put together a test project and can verify I get exactly the same error as you do.

When I need to filter out the root nodes in a tree, I use a fetch predicate of parent == nil instead of a transient attribute.

I understand your reaction - I too wanted way of having an attribute specifically called isRoot too. My guess is it's possible, but it'd take so much code it's just not worth the hassle.

Oh, and if you're dealing with core data any more than a little, mogenerator will make your life much easier.

John Gallagher
John - An unfortunate limitation but I suppose it makes some sense, since the fetch predicate appears to be run within the store. I'll check into mogenerator, too. A big thanks to you for going the extra mile!
Adam Preble
As an aside, it does make me wonder what the value of transients is, then. That is, do they have any advantage over properties one adds manually to their model classes?
Adam Preble
I've not really enough experience with Cocoa to tell you definitively, but my guess is the value is for displaying calculated values to the user, rather than filtering by them. You might want a table view to show full names but only have a firstName and lastName field, for example. I've not had time to delve deeper into the possibilities, but I'm sure they've got other uses too.
John Gallagher