4

I've been banging my head against the wall with this bug. Basically relationships aren't being merged from one context to another correctly.

Let's say I have two entity types in my model, Student and Teacher. A student can only have one teacher and a teacher can obviously have many students. I set this up correctly in my xcdatamodel class.

Because I'm syncing these entities from the server I have 2 Managed Object Contexts - one called uiContext which has it its concurrency type set to NSMainQueueConcurrencyType and another called backgroundContext with its concurrency type set to NSPrivateQueueConcurrencyType.

I've also set up a nested structure using [self.uiContext setParentContext:self.backgroundContext];

So this is what I'm doing:

1) Fetch Teachers from the server

2) Create managed objects for them in backgroundContext

3) Call save on backgroundContext

4) I listen for the NSManagedObjectContextDidSaveNotification, and execute the following code:

[self.uiContext performBlock:^(void) {
   [self.uiContext mergeChangesFromContextDidSaveNotification:notif];
}];

So far this works just fine. My uiContext gets the Teacher managed objects and my UI updates accordingly.

It breaks down though when I do the following:

5) Fetch the student data from the server

6) Create managed objects for them in the backgroundContext and set the relationship from student to the appropriate teacher.

7) Call save on backgroundContext

8) Since I'm listening for the NSManagedObjectContextDidSaveNotification notification this save should get merged into the uiContext

However, here's where I start experiencing issues. Even though the student objects are merged into the uiContext correctly, the relationship from teacher to student is not set. The relationship from student to teacher is correctly set but not the other way around. Specifically, [someTeacherObject.students count] == 0 but someStudentObject.teacher != nil

The relationships are set correctly in both the backgroundContext and the persistent store (verified). Just not in the uiContext.

Any ideas what could be going on? Any idea how to better debug this?

aloo
  • 5,331
  • 7
  • 55
  • 94
  • In your data model, is the student entity's `teacher` relationship the inverse of the teacher entity's `students` relationship? – Tom Harrington Jan 18 '13 at 21:06
  • Yes that is set correctly. And I believe it must be because the data is setup correctly in the backgroundContext and in the persistant store. – aloo Jan 18 '13 at 23:28
  • If that's the case then it should be impossible to have `[someTeacherObject.students count] == 0` and `someStudentObject.teacher != nil`-- unless `someStudentObject.teacher` is actually pointing to a different teacher object than the `someTeacherObject` you're checking. As inverse relationships, setting the one to non-nil would force the other to have a count > 0. Are you sure you're comparing the same instances? – Tom Harrington Jan 18 '13 at 23:43
  • Ok did some more digging and you are correct, it seems like they are different teacher objects. I believe this is because the objectIds are different if I log `someTeacherObject.objectId` vs `someStudentObject.teacher.objectId` – aloo Jan 20 '13 at 17:10
  • If I have a teacher object in my backgroundContext and merge it into my uiContext, it should have the same objectId correct? – aloo Jan 20 '13 at 17:11
  • @aloo Did you solve the problem yet? – Hans One Aug 01 '14 at 08:17

1 Answers1

0

As far as I'm aware, once you have a parentContext set for your background context, you no longer need to listen out for changes and merge them manually. You do, however, need to perform an extra saveContext: on your main context to save the changes to the store.

See the 'Parent/Child Contexts' section of this article on multi-context Core Data for more information.

Edit

Taken from that article, the code would look something like:

[backgroundContext performBlock:^{
// do something that takes some time asynchronously using the bg context

   // push to parent
   NSError *error;
   if (![backgroundContext save:&error])
   {
       // handle error
   }

   // save parent to disk asynchronously
   [mainMOC performBlock:^{
        NSError *error;
        if (![mainMOC save:&error])
        {
           // handle error
        }
    }];
}];
James Frost
  • 6,960
  • 1
  • 33
  • 42
  • I actually have it the other way around. My backgroundContext is the parent of my uiContext (see my code snippet in the question). – aloo Jan 20 '13 at 15:53
  • So I do need to merge changes made to my backgroundContext into my uiContext if I want to make changes to my backgroundContext – aloo Jan 20 '13 at 15:54