Markus Millfjord
Markus Millfjord

Reputation: 707

Recommended merge policies on main context & private context in multi-threaded CoreData app

I've read and tried to understand the recommended praxis of doing this, but I would like to have Your expert opinions on the following scenario;

I use CoreData and have a main context assigned to the persistent store coordinator.

- (void) setupCoreDataStack
{
    self.managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];

    NSURL *url = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"Model.sqlite"];

    NSDictionary *options = @{NSPersistentStoreFileProtectionKey: NSFileProtectionComplete,
                              NSMigratePersistentStoresAutomaticallyOption:@YES};
    NSError *error = nil;
    NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:options error:&error];
    if (!store)
    {
        NSError *deleteError = nil;
        if ([[NSFileManager defaultManager] removeItemAtURL:url error:&deleteError])
        {
            error = nil;
            store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:options error:&error];
        }

        if (!store)
        {
            // Also inform the user...
            NSLog(@"Failed to create persistent store. Error %@. Delete error %@",error,deleteError);
            abort();
        }
    }

    self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    self.mainManagedObjectContext.persistentStoreCoordinator = psc;
}

All my async worked threads (sync with backend, perform various BLE/CoreBluetooth actives, etc) create their own private contexts (from within their async dispatched threads), and then use performBlock to perform the work before finally saving private context AND main context according to guidelines/recommendations;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                         (unsigned long)NULL), ^(void)
{
    //Create private context and lnk to main context..
    NSManagedObjectContext* privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

    //Link private context to main context...
    privateContext.parentContext = self.mainManagedObjectContext;

    //Perform login-mechanism in block
    [privateContext performBlock:^{

        //Do the work!
        ...
        ...

        //Merge changes to main context!
        NSError* error = nil;
        if (![privateContext save:&error])
            abort();

        //Save main context to persistent store coordinator
        [self.mainManagedObjectContext performBlockAndWait:^{

            NSError *mainError;
            if (![self.mainManagedObjectContext save:&mainError])
                abort();
        }];
    }];
});

My question now is, how does the merge policies work in this setup? When I assign the mainContext a mergePolicy, does that apply to how changes are merged from mainContext to PSC, or when private contexts are merged to main context?

Is my understanding correct, that using NSMergeByPropertyObjectTrumpMergePolicy in this setup, if assigned to the private contexts, will ensure that the private context changes are merged to main context and that the private object will be used if there's a conflict? Or do I have to set the main context's mergePolicy of this to work too?

Thank you for your thoughts, /Markus

Upvotes: 2

Views: 1547

Answers (1)

Marcus S. Zarra
Marcus S. Zarra

Reputation: 46728

Generally you want to set the merge policy on the main NSManagedObjectContext. However my first question is, are you getting merge conflicts? If you are not getting any then do you need to change the merge policy at all?

I tend to leave the default policy (error on merge) in place until I actually hit a merge problem. Otherwise you could be potentially masking a problem by the merge working when you didn't realize there was a merge situation.

Upvotes: 4

Related Questions