Background:
In my app, I'm specifically targeting Mac OS X Lion. This issue involves Core Data, an NSPopover and a child NSManagedObjectContext (created by using the new parentContext property of NSManagedObjectContext).
I have a table of NSManagedObjects of class "Location". There's an Add button that calls addLocation: and if a table row is double-clicked, I call tableViewDoubleClick:.
For either case, what I do is create a new NSManagedObjectContext and set its parent context to that of the document's context. I then either create a new Location in that context or fetch the Location to be edited from the temporary context. I set the popover's representedObject property to the location in question. If I cancel the popover, nothing is saved. If the user clicks a Save button in the popover, I just call save: on the temporary context and the changes get pushed to the main context.
addLocation:
- (IBAction)addLocation:(id)sender
{
LocationEditViewController *popupController = [[[LocationEditViewController alloc] init] autorelease];
popupController.title = @"Add New Location";
NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
tempContext.parentContext = self.document.managedObjectContext;
Location *tempLocation = [NSEntityDescription insertNewObjectForEntityForName:@"Location" inManagedObjectContext:tempContext];
popupController.representedObject = tempLocation;
popupController.managedObjectContext = tempContext;
[popupController.popover showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxYEdge];
}
tableViewDoubleClick:
- (void)tableViewDoubleClick:(id)sender
{
NSInteger selectedRow = [self.table selectedRow];
if (selectedRow != -1)
{
NSRect rectOfSelectedRow = [self.table rectOfRow:selectedRow];
LocationEditViewController *popupController = [[[LocationEditViewController alloc] init] autorelease];
popupController.title = @"Edit Location";
Location *locationToEdit = [self.locationController.selectedObjects objectAtIndex:0];
NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
tempContext.parentContext = self.document.managedObjectContext;
Location *tempLocation = (Location *)[tempContext fetchObjectEqualTo:locationToEdit]; // Custom fetch helper method
popupController.managedObjectContext = tempContext;
popupController.representedObject = tempLocation;
[popupController.popover showRelativeToRect:rectOfSelectedRow ofView:sender preferredEdge:NSMaxXEdge];
}
}
Here's the problem that I'd like an explanation for:
The text fields in the popover are connected to the popover's representedObject via bindings in the nib. These work perfectly with a new object (addLocation:).
If the Location is an existing object (tableViewDoubleClick:), the bindings work well enough to pre-populate the fields with the Location's properties. However, changing the text in the fields does not alter the Location's properties at all. When the Save button in the popup is clicked, I tried logging the Location's properties before saving the temporary context. If it's an existing object, whatever I type into the fields isn't being reflected in the Location's properties - as if the bindings are only communicating one-way.
My workaround: I found that if I skip the bindings and just manually set the Location's properties to the values in the text fields before the save, that the changes do take effect.
- (IBAction)popoverSave:(id)sender
{
// These two methods always work. But if I remove these and use bindings instead, it only works for NEW Locations.
[(Location *)self.representedObject setLabel:self.labelField.stringValue];
[(Location *)self.representedObject setLocation:self.locationField.stringValue];
NSLog(@"representedObject = %@", self.representedObject);
NSError *error = nil;
[self.managedObjectContext save:&error];
[self.popover close];
}
I'd really like to know why this is the case, just in case I'm actually doing something wrong.
Thanks!