Core Data relationship not saving even though it is set

core-data, ios, iphone, nsmanagedobjectcontext, save

Solution

It's hard to be certain but here's a scenario that fits the symptoms:

- You look up an existing `GenericCall` instance before creating a new `PlaylistCall`. But this `GenericCall` already has a non-`nil` value for `playlistCall`.

- You make the assignment `playlistCall.genericCall = genericCall` using the new `PlaylistCall`.

- The already-existing `PlaylistCall` instance has its `genericCall` relationship set to `nil`-- because this is a one-to-one relationship and that's the default behavior in this case.

- Saving fails-- not because of the new `PlaylistCall` you created, which is fine, but because of the previous `PlaylistCall` that has been left with a dangling `genericCall` relationship.

If this is what's happening, you'll need to check for an existing `playlistCall` and do something reasonable with it before saving changes-- most likely either reassigning its `genericCall` to point to something else, or possibly just deleting the instance.

Problem

I have a mandatory `genericCall` relationship for a `PlaylistCall` entity that is being set prior to the `PlaylistCall` being saved. The logs show that the `genericCall` is not nil and yet I get a validation error when trying to save the managed object context stating that the relationship is not set. What could cause a relationship that is already set to then become nil during the save? What I have verified or attempted: - I use a shared singleton as my managed object context for the entire app so I am definitely working within the same managed object context - An inverse relationship exists between `GenericCall` and `PlaylistCall` - On a whim, I tried deleting the relationship and then recreating it, but that did nothing so it seems like a bug in my code instead of a random bug in Xcode - I tried using `setValue:forKey` instead of the dynamic property accessors (the dot notation) with no change Here are the logs: ``` 2013-03-25 11:48:35.400 Project[30599:c07] Supposed to add <PlaylistCall: 0xa1be450> (entity: PlaylistCall; id: 0xa17dea0 <x-coredata:///PlaylistCall/tBB209BC5-EF9A-4DAE-9A55-0001B97499392> ; data: { delaySetting = 0; genericCall = "0xd4ad520 <x-coredata://7EB1AFB2-7B49-431E-9700-275307B4D1C1/GenericCall/p367>"; orderInTable = 4; playlist = nil; repeatSetting = 0; repeatType = 0; volumeSetting = 100; }) with generic call <GenericCall: 0xd4c3fd0> (entity: GenericCall; id: 0xd4ad520 <x-coredata://7EB1AFB2-7B49-431E-9700-275307B4D1C1/GenericCall/p367> ; data: { animalCategory = "0xc0aaa80 <x-coredata://7EB1AFB2-7B49-431E-9700-275307B4D1C1/AnimalCategory/p19>"; callDescription = "Call Name"; numberOfTimesOnPlaylist = 0; numberOfTimesPlayed = 0; playlistCall = "0xa17dea0 <x-coredata:///PlaylistCall/tBB209BC5-EF9A-4DAE-9A55-0001B97499392>"; soundFile = "sound_file_name"; timeLength = "0.21"; }) 2013-03-25 11:48:35.402 Project[30599:c07] Error saving. Reason(s): PlaylistCall: The attribute 'genericCall' cannot be empty. ``` Here is how I am setting and saving the values: ``` GenericCall *genericCall = [self.fetchedResultsController objectAtIndexPath:indexPath]; PlaylistCall *playlistCall = (PlaylistCall *)[NSEntityDescription insertNewObjectForEntityForName:@"PlaylistCall" inManagedObjectContext:commonContext.managedObjectContext]; [self.playlist addPlaylistCall:playlistCall withGenericCall:genericCall orderInTable:[self.playlist.playlistCalls count]]; // Note: all other attributes are setup via model defaults [commonContext save]; ``` My NSManagedObject subclass for `Playlist` where I set the relationship: ``` - (void)addPlaylistCall:(PlaylistCall *)playlistCall withGenericCall:(GenericCall *)genericCall orderInTable:(int)orderInTable { NSMutableOrderedSet *tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.playlistCalls]; playlistCall.genericCall = genericCall; playlistCall.orderInTable = [NSNumber numberWithInt:orderInTable]; NSLog(@"Supposed to add %@ with generic call %@", playlistCall, playlistCall.genericCall); [tempSet addObject:playlistCall]; self.playlistCalls = tempSet; } ``` My common managed object context singleton (`CommonContext`) just looks like this (and yes I've verified that the `self.managedObjectContext` is not nil and doesn't change): ``` // Saves the managed object context - (BOOL)save { NSError *anError; if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&anError]){ [self handleSaveError:anError]; return NO; } else { return YES; } } ``` Any ideas for what I may be doing wrong? It seems straightforward and I use this same approach everywhere else in my app for setting relationships, so there has to be something that I am missing. Thanks for your time!

Original source