Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iPhone iOS how to merge Core Data NSManagedObjectContext?

I'm trying to download some JSON objects in the background and am doing quite a bit of multi threading. Once the operation completes, I noticed that this assertion fails:

NSAssert([user.managedObjectContext isEqual:[AppUser managedObjectContext]],@"Different contexts");

How can I merge changes into the main context defined by [AppUser managedObjectContext] ?

like image 268
Alex Stone Avatar asked Dec 17 '22 00:12

Alex Stone


2 Answers

I really suggest you to read the following link on importing-and-displaying-large-data-sets-in-core-data by Marcus Zarra.

When you deal with threads, each thread you create needs to have its own context as written in the link which jrturton provided. Then if you want to merge changes between a main context (the one you created in the main thread) and the other context (the one you use in the context), you need to listen for NSManagedObjectContextDidSaveNotification in the main thread

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextHasChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];

and do the merge like

- (void)contextHasChanged:(NSNotification*)notification
{
  if ([notification object] == [self mainObjectContext]) return;

  if (![NSThread isMainThread]) {
    [self performSelectorOnMainThread:@selector(contextHasChanged:) withObject:notification waitUntilDone:YES];
    return;
  }

  [[self mainObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}

The notification object contains the changes you made in the thread context.

Some notes

Threading is difficult to achieve. The link I provided uses an NSOperation with its context. This is quite simple to set up but since iOS 5 there are some functionalities that make your life easier.

For example to create a context in a different thread you can do like the following:

// create a context with a private queue so access happens on a separate thread.
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// insert this context into the current context hierarchy
context.parentContext = context;
// execute the block on the queue of the context
[context performBlock:^{

      // do your stuff (e.g. a long import operation)

      // save the context here
      // with parent/child contexts, saving a context pushes the changes out of the current context
      NSError* error = nil;
      [context save:&error];
}];

In addition you can see the doc for UIManagedDocument. This class integrates well for Core Data and allows you to avoid using the Core Data stack.

Hope it helps.

like image 53
Lorenzo B Avatar answered Jan 01 '23 10:01

Lorenzo B


Your particular situation (downloading JSON in the background) is quite common. I have seen a number of implementations that make do without multiple contexts.

In many cases, the simplest and by far the robustest way is to have the objects that do the downloads notify the main thread via protocol or notification of a finished download. You then do the saving on the main thread.

like image 40
Mundi Avatar answered Jan 01 '23 10:01

Mundi