1

I'm creating temportary contexts in a private queue to asynchronously update the data I persist with Core Data:

NSManagedObjectContext *privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
privateContext.persistentStoreCoordinator = appDelegate.persistentStoreCoordinator;

[privateContext performBlock: ^{

    // Parse files and/or call services and parse
    // their responses

    dispatch_async(dispatch_get_main_queue(), ^{
        // Notify update to user
    });
}];

Then, once I've got the new data, I need to merge the changes with my main context. I know this is a common scenario, but I'm not sure how should I proceed... This Apple's Core Data documentation section talks about setting a merge policy and I don't fully understand the way to handle that. On the other hand, I found this link, where my scenario is described in its "Stack #2" section and what it says looks simpler, and it doesn't talk about merge policies at all...

Which the correct or most appropriate way should be? I'd really appreciate an explanation and/or simple example of how to manage this scenario.

Thanks in advance.

AppsDev
  • 12,319
  • 23
  • 93
  • 186

1 Answers1

0

What you have there looks pretty good.

You are using a private queue to do your work, and it's being saved to the persistent store.

If you only have a small number of changes, then you will be fine. In that case, you want to handle the NSManagedObjectContextDidSaveNotification for your context, and merge the changes into your other context with

[context mergeChangesFromContextDidSaveNotification:notification];

However, if you are really doing a lot of changes, you probably want a separate persistent store coordinator (attached to the same store). By doing this, you can write to the store, while MOCs on the other PSC are reading. If you share the PSC with other readers, only one will get access at a time and you could cause the readers to block until your write has finished.

Also, if you are doing lots of changes, make sure you do them in small batches, inside an autoreleasepool, saving after each batch. Take a look at this similar question: Where should NSManagedObjectContext be created?

Finally, if you are doing lots of changes, it may be more efficient to just refetch your data than it will be to process all the merges. In that case, you don't need to observe the notification, and you don't need to do the merge. It's pretty easy. Note, that if you do this, you really should have a separate PSC... especially if you want to save and notify in small-ish batches.

[privateContext performBlock: ^{

    // Parse files and/or call services and parse
    // their responses

    dispatch_async(dispatch_get_main_queue(), ^{
        // Refetch the data you want... if on iOS, this is likely as simple
        // as telling the fetched results controller to refetch, and
        // reloading your table view (or whatever else is using the data).
    });
}];
Community
  • 1
  • 1
Jody Hagins
  • 27,943
  • 6
  • 58
  • 87
  • Thanks! Currently I'm not doing a lot of changes in data updates, but this may increase in a future so I think I'd go for the approach of refetching the data instead of merging changes... A question now arises to me: what is the difference between creating a private context and directly using it as I'm currently doing, and setting it as child context? – AppsDev Aug 27 '15 at 10:15
  • @AppsDev That's a huge question in itself. The best answer I can give in the space for a comment is that I only use nested contexts for my main MOC (and that is because I have my own setup for asynchronous fetching which can then populate up to the main MOC). I do all of my saving on a separate MOC, attached directly to its own PSC. I manage merges just as I described above... do merges on saves if it's small, refetch if it's large. – Jody Hagins Aug 27 '15 at 14:41
  • Thank you. Regarding creating context's own PSC... is it needed to also create a new `NSPersistentStore` or is there a way to use the one in main thread I already have (the one created in `AppDelegate` by default)? – AppsDev Aug 30 '15 at 16:14
  • And another question... what if within the `performBlock:` I call asynchronous web services? How could I wait until I have the response of all of them to refetch the data in the main thread? – AppsDev Aug 30 '15 at 16:17
  • @AppsDev I create a completely new stack. If you have a series of async events, just refresh when they are all done. You can simply call `dispatch_async(dispatch_get_main_queue, BLOCK)` and do your refresh from anywhere. – Jody Hagins Aug 30 '15 at 16:54
  • But won't creating a new persistent store object with the same URL cause problems? – AppsDev Aug 30 '15 at 17:14
  • If your store is SQLite, it will handle any synchronization for you. In fact, you get better reader/writer synchronization because when sharing a PSC, it locks any reader/writer for another. With WAL (which is on by default in iOS7+), you can have one writer and as many readers executing concurrently. – Jody Hagins Aug 30 '15 at 20:15
  • Is it possible/correct to set that private context with its own Core Data stack as child of the main context? How is/should be then the merging be handled? – AppsDev Sep 12 '15 at 16:53
  • No. You should either assign a MOC directly to a PSC or set its parent MOC, but not both. – Jody Hagins Sep 14 '15 at 08:55