I am trying to use AFIncrementalStore together with MagicalRecord.
I have a class Beer with few properties (from BreweryDB web service). To fetch beers I am using [Beer MR_fetchAllWithDelegate:self] which returns NSFetchedResultsController instance, (which I grab and assign to beerFetchController) and performs an actual fetch using that controller. As you can also see I set my view controller as a delegate of the the method, which in turns, sets that view controller as a delegate of the beerFetchController. So far so good. I have a table view that displays the beers, and I implement the method
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.beersTable reloadData];
}
So whenever the AFIncrementalStore gets the data, puts it into the context, the controller is notified and in turn this method should be called, and the table will be refreshed. The problem is, this delegate method is not called at all! After some investigation i was able to draw some conclusions.
+ (NSFetchedResultsController *) MR_fetchAllWithDelegate:(id<NSFetchedResultsControllerDelegate>)delegate;
{
return [self MR_fetchAllWithDelegate:delegate inContext:[NSManagedObjectContext MR_contextForCurrentThread]];
}
+ (NSFetchedResultsController *) MR_fetchAllWithDelegate:(id<NSFetchedResultsControllerDelegate>)delegate inContext:(NSManagedObjectContext *)context;
{
NSFetchRequest *request = [self MR_requestAllInContext:context];
NSFetchedResultsController *controller = [self MR_fetchController:request delegate:delegate useFileCache:NO groupedBy:nil inContext:context];
[self MR_performFetch:controller];
return controller;
}
The issue is that, [NSManagedObjectContext MR_contextForCurrentThread], will return a child context who's parent is the backing context that AFIncrementalStore uses internally. AFIncrementalStore saves to the parent, not the child, therefore the controller has no idea that anything changed, and the delegate method is not called. I was able to modify the method like this :
+ (NSFetchedResultsController *) MR_fetchAllWithDelegate:(id<NSFetchedResultsControllerDelegate>)delegate inContext:(NSManagedObjectContext *)context;
{
NSFetchRequest *request = [self MR_requestAllInContext:context];
NSFetchedResultsController *controller = [self MR_fetchController:request delegate:delegate useFileCache:NO groupedBy:nil inContext:context.parentContext];
[self MR_performFetch:controller];
return controller;
}
Instead of binding the controller to context returned by MR_contextFromCurrentThread, I bind it to its parent. And then, as expected stuff works as it should, context is changed, controller sees that, and the delegate method is called. The question is, is there any way to, push the changes from the parent context to the child context so I do not have to change the method implementation ?
+ (NSManagedObjectContext *) MR_contextForCurrentThread;
{
if ([NSThread isMainThread])
{
return [self MR_defaultContext];
}
else
{
NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
NSManagedObjectContext *threadContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextKey];
if (threadContext == nil)
{
threadContext = [self MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]];
[threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey];
}
return threadContext;
}
}