A few days ago I spent a lot of time tracking down a data validation error in Core Data which turned out to have a very simple cause.
The application I’m working on has two entities for customer and order. Each order must be linked to a single customer.
When a new order is saved, I call this function to save changes:
- (void)saveContext {
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
}
}
The first time I created a new order, I was able to save it successfully. The next time I tried to save an order, I got a validation error. When I inspect the new order I just created, it looks correct, with a proper customer relationship.
Looking at the NSManagedObjectContext in the debugger offers a few clues. You’ll see that it has sets of inserted & changed items, which you can inspect by typing ‘po managedObjectContext->_insertedObjects‘ or ‘po managedObjectContext->_changedObjects‘.
The set of inserted object contained exactly what I expected, but the set of changed items contained some unexpected items, including the previous order, which now had its customer relationship set to nil. Since the customer is required, the data validation failure was not on our newly inserted item, but on the previous order which used to be valid.
How could I be changing that previous order? I couldn’t find any place in my code which could have been doing it. Instead it turned out to be a simple error in my data model.
When you create a relationship, it can be either required or optional, and it can be to a single entity or to many entities. Every relationship also needs to have an inverse relationship.
In this case, my order had a required to-one relationship with a customer, which meant customer also had to have an inverse relationship to orders, which was optional. However, I neglected to set my customer to order relationship as to-many.
Therefore, although I never touched the customer’s relationship to orders, when I created a new order & set the customer, Core Data also set the customer’s order relationship. Since I didn’t make it to-many, doing so wiped out the relationship to the previous order, which also wiped out that order’s required relationship to a customer.
Moral of this story: check your relationships carefully and make sure you set the proper attributes for the inverse relationship. In this case, I spent many hours debugging something caused by setting a checkbox incorrectly in the model editor.