Jack Freeman Jack Freeman - 29 days ago 6
Objective-C Question

CoreData Threading. iOS 6 vs 7

I'm having an issue related to my core data implementation and threading. It works fine on iOS 7 but I can't get the main context to refresh properly on iOS 6.

I have two NSPersistentStoreCoordinators pointing to the same database file. I then have a main context and a background context. All new child contexts are children of the background context.

When I update items on child contexts in background threads on 6 they never merge to the main context until I restart the app and the main context fetches them from the store. On ios7 this all works fine however. How can I ensure the main context is refreshing the merge properly without faulting and reloading the db?

I know iOS 7 turned on asynchrounous sql access by default, but I've done that on 6 with:

NSSQLitePragmasOption : @{@"journal_mode" : @"WAL"}


Here is how my context's are set up:

self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[self.mainManagedObjectContext setPersistentStoreCoordinator:self.mainPersistentStoreCoordinator];
[self.mainManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
[self.mainManagedObjectContext setUndoManager:nil];
self.backgroundParentContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[self.backgroundParentContext setPersistentStoreCoordinator:self.backgroundPersistentStoreCoordinator];
[self.backgroundParentContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
[self.backgroundParentContext setUndoManager:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.backgroundParentContext];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mainContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.mainManagedObjectContext];


Here is how I create a child context:

- (NSManagedObjectContext *)newChildManagedObjectContext
{
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSPrivateQueueConcurrencyType];

[childContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];

[childContext setParentContext:self.backgroundParentContext];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(managedObjectContextDidSave:)
name:NSManagedObjectContextDidSaveNotification object:childContext];
[childContext setUndoManager:nil];
return childContext;
}


Here are the notification methods:

- (void)mainContextDidSave:(NSNotification *)notification
{
__block NSNotification *strongNotification = notification;
[self.backgroundParentContext performBlockAndWait:^{
[self.backgroundParentContext mergeChangesFromContextDidSaveNotification:strongNotification];
}];
}

- (void)backgroundContextDidSave:(NSNotification *)notification
{
[self.mainManagedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:NO];
}

- (void)managedObjectContextDidSave:(NSNotification *)notification
{
[self.backgroundParentContext performBlockAndWait:^{
NSError *error = nil;
if (![self.backgroundParentContext save:&error]) {
DNSLog(@"error saving context: %@", error);
}
}];
}


UPDATE:

Well it appears this is just never going to work on 6. According the docs, they have changed quite a bit around merging contexts for 7. So I'm going to have to take a different approach for 6 I guess...

Answer

I haven't been using two NSPersistentStoreCoordinators on iOS 6, but merging changes between contexts from different store coordinators was mentioned explicitly during WWDC, so I think it's changed in iOS 7. See the following sessions:

  • 207 - What’s New in Core Data (starting from slide number 145).
  • 211 - Core Data Performance (starting from slide number 91),
Comments