I'm using RestKit to grab objects from my RoR service and using CoreData to persist some of the objects (more static-type lookup table objects). TasteTag is one of those persisted objects:
#ifdef RESTKIT_GENERATE_SEED_DB
NSString *seedDatabaseName = nil;
NSString *databaseName = RKDefaultSeedDatabaseFileName;
#else
NSString *seedDatabaseName = RKDefaultSeedDatabaseFileName;
NSString *databaseName = @"Model.sqlite";
#endif
RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:kServerURL];
manager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:databaseName usingSeedDatabaseName:seedDatabaseName managedObjectModel:nil delegate:self];
.. lots of fun object mapping ..
RKManagedObjectMapping* tasteTagMapping = [RKManagedObjectMapping mappingForClass:[TasteTag class]];
[tasteTagMapping mapKeyPath:@"id" toAttribute:@"tasteTagID"];
[tasteTagMapping mapKeyPath:@"name" toAttribute:@"name"];
tasteTagMapping.primaryKeyAttribute = @"tasteTagID";
[[RKObjectManager sharedManager].mappingProvider setMapping:tasteTagMapping forKeyPath:@"taste_tags"];
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:tasteTagMapping];
.. some more mapping ..
I have the data coming back from the RoR server and it's getting mapped to objects as expected. The Core Data entity also seems mapped fine after RestKit gets the request back:
"<TasteTag: 0x6e87170> (entity: TasteTag; id: 0x6e85d60 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5> ; data: <fault>)"
The issue is when I try to access properties on the objects the fault can't seem to be fire. At first I was just calling the properties, which always came back as nil (even though that should fire the fault):
for (TasteTag *tag in self.vintage.tasteTags) {
[tagNames addObject:tag.name]; //get error of trying to add nil to array
}
After looking into manually triggering faults (http://www.mlsite.net/blog/?p=518) I tried calling [tag willAccessValueForKey:nil]
which results in:
Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x6e7b060 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5>''
Looking up the entity in the .sqlite based on the key (TasteTag/p5) does show it mapped to the one I'd expect.
Other posts relating to RestKit recommend disabling the object cache (which I'm not using) since this is usually caused by an entity being deleted. But at this stage I'm just reading, not deleting, and I have no cache in place.
If I just call [TasteTag allObjects]
I'm able to get all the objects back fine and they load without issue. It's just in the case when they are faulted it seems.
Documenting my fix (read:hack) per Ryan's suggestion.
The error seems to be in how RestKit assumed you'll be using the objects returned from their objectLoader:didLoadObjects:
method. They seem to assume it will all be Core Data backed (and follow the flow similar to what Ryan talked about - let it sync to Core Data, then re-query) or that you'll be using all non Core Data backed objects and just keep those results around.
In my case I had a mix - a root array of non Core Data backed objects which each then contained an array of Core Data backed entities. The top-level objects are ones I don't mind querying the server for and have no reason to persist locally beyond the view they're shown in. It seems once objectLoader:didLoadObjects:
is complete the managed object context backing the Core Data entities within the objects
param is disposed of (under the assumption you'll be re-querying for them), causing any future calls to the entities to result in being treated as faults, even though you can't trigger the fault and load the data (results in NSObjectInaccessibleException
).
I got around it with an ugly hack - within objectLoader:didLoadObjects:
I access one of the Core Data entity's managed object context and copy it to a property within the view (self.context = [tag managedObjectContext];
). This prevents the context it being released after objectLoader:didLoadObjects:
is complete, allowing me to access the entities without issue later in the view.
Another solution would be to manually re-query for each entity using a new context and copy that back to the stored return objects. One could do this when one goes to display them, or possibly some post-processing in objectLoader:didLoadObjects:
, using a new context. The entity ID is still around on the faulted object so one could use that to re-query without issue even after the original RestKit context disappears. But it seems silly to have to re-query for every entity in the object graph like that.