Skip to content

Multiple Managed Object Contexts with Core Data

2008 July 9

In the last article, I walked through a simple multi-window Core Data document application. The pretext of this article is to show how I use multiple Managed Object Contexts in my Cocoa Core Data apps. We’ll be building on the application created in the last article by adding in two new windows; an ‘Add New Department’ window and an ‘Add New Employee’ window.

The application we created previously lets you insert new departments and people by clicking on the ‘+’ buttons before editing the values in the relevant table view. When you click the ‘+’ button, the new managed object that is created has blank string attributes that show up in the table view as an apparently invisible line. One way to change this behaviour would be to subclass NSManagedObject for our Department and Person entities and give a newly created object some default values. In this article, however, we’ll change the ‘Add’ behaviour to open yet another new window in which you can type the initial values before the new object is inserted into the list.

Here is a screenshot of the application as it will stand at the end of the article:

The finished application with multiple windows and managed object contexts

– Edit – The example project for this post is available to download from here: coredatamultiwin2.zip.

Multiple Managed Object Contexts

You will recall that a managed object context is like a scratchpad for managed objects, possibly representing data from the persistent store, or possibly being only temporary data that hasn’t yet been persisted. If the user is faced with a new document in our current multi-window application, they may create departments and employees that, until the document is saved, exist only ‘in memory’ and only within the managed object context. If we were to open a second main window for the previously unsaved document but give it a new managed object context, no objects at all would show up in the lists as the objects the user has created in the first main window exist only in that context and not in any other. Without communication between contexts, the objects that exist in one are independent of objects existing in the other.

We can use this independence in our application to give precisely the functionality we need. The desired behaviour is to let the user create a new department or employee by typing in the values they wish before adding the object to the lists in the main window. This will avoid having the blank unedited objects appear on object creation. When the user opens the ‘New Department Window’, we will create a new, blank department in a second MOC that they can edit as they wish, before clicking an ‘Add New Department’ button which will insert the new department into the main managed object context so that it appears in the main window’s department list. Obviously, this functionality could be achieved by having code that simply read values from relevant unbound text fields on our ‘Add’ window before creating a new managed object in the main context, but by utilising a second MOC we get all sorts of features (like Undo) included without any extra code.

Another NSWindowController

We’ll start by implementing the code for the Add Department window controller. Create another NSWindowController subclass called AddDepartmentController. In the AddDepartmentController.h header file, add attributes for two NSManagedObjectContexts and an NSManagedObject together with relevant KVC setters, getters and an initializer. My interface looks like this:

@interface AddDepartmentController : NSWindowController {
 
	NSManagedObjectContext *_moc, *_docMoc;
	NSManagedObject *_dep;
 
}
 
- (NSManagedObjectContext *)managedObjectContext;
 
- (void)setDocumentManagedObjectContext:(NSManagedObjectContext *)value;
- (NSManagedObjectContext *)documentManagedObjectContext;
 
- (void)setDepartmentObject:(NSManagedObject *)value;
- (NSManagedObject *)departmentObject;
 
- (AddDepartmentController *)initWithDocumentManagedObjectContext:(NSManagedObjectContext *)inDocMoc;
 
@end

We will provide ‘lazy’ access to the managedObjectContext attribute by generating it only when it is requested so there is no setter for this attribute.

Open AddDepartmentController.m and write the code:

@implementation AddDepartmentController
 
- (AddDepartmentController *)initWithDocumentManagedObjectContext:(NSManagedObjectContext *)inDocMoc
{
	self = [super initWithWindowNibName:@"AddDepartmentWindow"];
 
	[self setDocumentManagedObjectContext:inDocMoc];
	return self;
}
 
- (void)setDocumentManagedObjectContext:(NSManagedObjectContext *)value
{
	// keep only weak ref
	_docMoc = value;
}
 
- (NSManagedObjectContext *)documentManagedObjectContext
{
	return _docMoc;
}
 
- (void)setDepartmentObject:(NSManagedObject *)value
{
	// keep only weak ref
	_dep = value;
}
 
- (NSManagedObject *)departmentObject
{
	return _dep;
}
 
@end

This code is fairly self-explanatory and you’ll notice that the implementation to access our second NSManagedObjectContext has not yet been written. As we’re providing lazy access to the new managedObjectContext, the method should first check to see if the _moc value has previously been set. If not, it will allocate some memory and initialize a new context before setting the persistent store to that of the existing context. Add this code to AddDepartmentController.m:

- (NSManagedObjectContext *)managedObjectContext
{
	if (_moc == nil)
    {
        _moc = [[NSManagedObjectContext alloc] init];
        [_moc setPersistentStoreCoordinator:[[self documentManagedObjectContext] persistentStoreCoordinator]];
    }
    return _moc;
}

We only want the user to be able to see one ‘Add Department’ window at a time which means we need to make sure that there is only ever one instance of the AddDepartmentController class per document.

Open MyDocument.h, #import the AddDepartmentController.h and add an instance of the controller to the document. Add a method openAddDepartmentWindow: to the existing declaration so that your code looks like this:

#import <cocoa/Cocoa.h>
#import "AddDepartmentController.h"
 
@interface MyDocument : NSPersistentDocument {
 
	IBOutlet NSArrayController *peopleArrayController;
	IBOutlet NSArrayController *departmentsArrayController;
 
	AddDepartmentController *_addDeptCtl;
 
}
 
- (IBAction)openPersonWindow:(id)sender;
- (IBAction)openAddDepartmentWindow:(id)sender;
 
@end

Open MyDocument.m and implement the openAddDepartmentWindow: method thus:

- (IBAction)openAddDepartmentWindow:(id)sender
{
	if( _addDeptCtl == nil )
	{
		_addDeptCtl = [[AddDepartmentController alloc] initWithDocumentManagedObjectContext:[self managedObjectContext]];
		[_addDeptCtl setShouldCloseDocument:NO];
		[self addWindowController:_addDeptCtl];
	}
	[_addDeptCtl showWindow:sender];
}

This code checks to see whether the MyDocument _addDeptCtl attribute has previously been set and if not, it creates the window passing in our main managedObjectContext for the controller’s documentManagedObjectContext attribute. showWindow: is then called to bring it to the front.

The Interface

Now that we have the AddDepartmentController class written, we need to set up the nib file for it to load.

  • Create a new Interface Builder window nib file called ‘AddDepartmentWindow.nib’.
  • Set the class of the File’s Owner to be ‘AddDepartmentController’.
  • Connect the ‘window’ outlet of File’s Owner to the Window in the nib.
  • Connect the ‘delegate’ outlet of the Window to File’s Owner.
  • Add a label, text field and button to the window to make it look like this:
    The Add New Department window layout in Interface Builder
  • Open MyDocument.nib in Interface Builder. Change the action of the department ‘+’ button so that it triggers the ‘openAddDepartmentWindow:’ action.

Return to Xcode and build and run the project. Clicking on the ‘+’ button for the Departments table view should open the ‘Add New Department’ window and if you click that ‘+’ button more than once, only a single window will open. The ‘Add Department’ button in the window doesn’t do anything yet as we haven’t written any code for adding departments and there is also a problem with the window being given the same name as the main document window (ie “Untitled” for a new document).

Let’s solve the document title problem first.

  • Open the AddDepartmentController.m file.
  • Implement a windowTitleForDocumentDisplayName: method with the following code:
    - (NSString *)windowTitleForDocumentDisplayName:(NSString *)displayName
    {
    	return [NSString stringWithFormat:@"Add Department to '%@'", displayName];
    }

Build and run the project again to check that the window now has the correct title.

Using the second managed object context

Our goal is to have the window controller create a new Department object in the second managed object context and allow the user to edit its name with the text field before ‘Add’ing it to the main context. We need to create this object in the initialization code for the controller; when the Add button is pressed, the object will be copied into the main context before being removed from the second context where a new, blank object will be created ready for the next time the window is used.

Open AddDepartmentController.m and change the initialization code to the following:

- (AddDepartmentController *)initWithDocumentManagedObjectContext:(NSManagedObjectContext *)inDocMoc
{
	self = [super initWithWindowNibName:@"AddDepartmentWindow"];
 
	[self setDocumentManagedObjectContext:inDocMoc];
 
	id newDepartment = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:[self managedObjectContext]];
 
	[self setDepartmentObject:newDepartment];
	return self;
}

Here we use the shortcut code provided by the NSEntityDescription class to insert a new Department object into the controller’s managedObjectContext.

So that we can make use of Undo facility provided by the second managed object context, implement this method in AddDepartmentController.m:

- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)sender
{
    return [[self managedObjectContext] undoManager];
}

Now open AddDepartmentWindow.nib in Interface Builder and make changes to use our new Department object:

  • Add an NSObjectController called ‘New Department’. Set its Mode to Entity and Entity Name to ‘Department’.
  • Bind the Managed Object Context of this controller to the File’s Owner ‘managedObjectContext’.
  • Bind the Content Object to the File’s Owner ‘departmentObject’ model key path.
  • In the AddDepartmentWindow window interface bind the Value of the department text field to the New Department controller’s ‘selection’ ‘name’ key path.

If you build and run the application, you should find that typing text in the text field of the Add Department window can be undone. You will, however, also find that there is one Undo past your last edit and this is the creation of the new object in the initializer.

To correct this problem, we need to disable the undo manager while we create the new object. Change initWithDocumentManagedObjectContext: to the following:

- (AddDepartmentController *)initWithDocumentManagedObjectContext:(NSManagedObjectContext *)inDocMoc
{
	self = [super initWithWindowNibName:@"AddDepartmentWindow"];
 
	[self setDocumentManagedObjectContext:inDocMoc];
 
	NSUndoManager *undoMgr = [[self managedObjectContext] undoManager];
    [undoMgr disableUndoRegistration];
 
	id newDepartment = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:[self managedObjectContext]];
 
	[[self managedObjectContext] processPendingChanges];
    [undoMgr enableUndoRegistration];
 
	[self setDepartmentObject:newDepartment];
 
	return self;
}

This code asks the managedObjectContext for its undoManager, disables Undo functionality, creates the new department, calls processPendingChanges: to make sure the creation has happened before it enables Undo again.

Build and run the application again to check that you can now only undo edits to the Department text field.

Copying to a new context

We still need to write the code to create the new Department object in the main managed object context so open AddDepartmentWindowController.h and declare an action for the button:

- (IBAction)addDepartmentToDocumentContext:(id)sender;

Implement the function in AddDepartmentWindowController.m with the following code and then connect it to the ‘Add Department’ button in Interface Builder:

- (IBAction)addDepartmentToDocumentContext:(id)sender
{
	[[self window] endEditingFor:nil];
 
	NSString *depString = [[self departmentObject] valueForKey:@"name"];
 
	NSUndoManager *docUndoMgr = [[self documentManagedObjectContext] undoManager];
	[docUndoMgr beginUndoGrouping];
 
    NSManagedObject *newDepartmentInDocument = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:[self documentManagedObjectContext]];
	[newDepartmentInDocument setValue:depString forKey:@"name"];
 
	[[self documentManagedObjectContext] processPendingChanges];
	[docUndoMgr endUndoGrouping];
 
	NSUndoManager *windowUndoMgr = [[self managedObjectContext] undoManager];
	[windowUndoMgr disableUndoRegistration];
 
	[[self managedObjectContext] deleteObject:[self departmentObject]];
 
	id newDepartmentInWindow = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:[self managedObjectContext]];
 
	[[self managedObjectContext] processPendingChanges];
 
	[windowUndoMgr enableUndoRegistration];
	[self setDepartmentObject:newDepartmentInWindow];
}

This method first of all tells the window to end editing. An NSTextField commits editing when the user presses Enter or tabs away from the field etc. In our New Department window, the user might not press Enter or tab before pressing the ‘Add New Department’ button so we need to commit the text field changes before reading the value back out from the object.

Once we’ve committed editing and found out what the new department name should be, we start an undo group on the main managed object context. This is so that the user can undo the new department creation and name change that’s about to happen in one go rather than undoing the two actions separately. After creating the new department into the main document context, we delete the old temporary department object and then create a new one (as we did in the initialization method) ready for any future additions.

If it bothers you that the department text field loses focus when you click the Add button, add an NSTextField IBOutlet called departmentTextField to your AddDepartmentWindowController.h file and set it to the department text field using Interface Builder. Then add this line at the end of the addDepartmentToDocumentContext: method:

[[self window] makeFirstResponder:departmentTextField];

Now when you click the Add button, you can start typing again immediately without having to click in the text field.

The Add Employee Window

The implementation for the Add Employee window controller will be very similar to that of the Add Department controller but there is one important difference: the Employee managed object has a relationship, namely to its Department.

As I’ve said on oh-so-many occasions, each managed object context is pretty much oblivious to any other context. So, we need at this point to make a decision as to how we’re going to handle the Department popup menu on a New Employee window. If nothing has yet been saved, a secondary MOC won’t pull any existing departments from the persistent store; if the user has input departments but not saved, we still need to find a way to display those ‘temporary’ department objects in our new employee window. We could force the main context to save to the persistent store, but that would force the user to save the whole document just to create an employee object. We could also copy across all the Department objects from the main context into our secondary context but this seems inefficient, and the user might add or delete a department in the main document window mid-way through adding an employee, leaving the Department popup with inconsistent values from the main window. What makes sense in this particular case, therefore, is to display the departments from the main context and the rest of the values from our secondary context.

Coding the Add Employee controller

Add a new NSWindowController subclass named AddEmployeeController to your project.

As the code is similar to before, I will for the moment simply show what should go in each file.

  • Make AddEmployeeController.h look like the following:
    @interface AddEmployeeController : NSWindowController {
     
    	NSManagedObjectContext *_moc, *_docMoc;
    	NSManagedObject *_emp;
     
    	IBOutlet NSPopUpButton *departmentsPopupButton;
     
    }
     
    - (NSManagedObjectContext *)managedObjectContext;
     
    - (void)setDocumentManagedObjectContext:(NSManagedObjectContext *)value;
    - (NSManagedObjectContext *)documentManagedObjectContext;
     
    - (void)setEmployeeObject:(NSManagedObject *)value;
    - (NSManagedObject *)employeeObject;
     
    - (AddEmployeeController *)initWithDocumentManagedObjectContext:(NSManagedObjectContext *)inDocMoc;
     
    - (IBAction)addEmployeeToDocumentContext:(id)sender;
     
    @end
  • Implement the methods in AddEmployeeController.m like this:
    @implementation AddEmployeeController
     
    - (AddEmployeeController *)initWithDocumentManagedObjectContext:(NSManagedObjectContext *)inDocMoc
    {
    	self = [super initWithWindowNibName:@"AddEmployeeWindow"];
     
    	[self setDocumentManagedObjectContext:inDocMoc];
     
    	NSUndoManager *undoMgr = [[self managedObjectContext] undoManager];
        [undoMgr disableUndoRegistration];
     
    	id newEmployee = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:[self managedObjectContext]];
     
    	[[self managedObjectContext] processPendingChanges];
        [undoMgr enableUndoRegistration];
     
    	[self setEmployeeObject:newEmployee];
     
    	return self;
    }
     
    - (void)setDocumentManagedObjectContext:(NSManagedObjectContext *)value
    {
    	// keep only weak ref
    	_docMoc = value;
    }
     
    - (NSManagedObjectContext *)documentManagedObjectContext
    {
    	return _docMoc;
    }
     
    - (NSManagedObjectContext *)managedObjectContext
    {
    	if (_moc == nil)
        {
            _moc = [[NSManagedObjectContext alloc] init];
            [_moc setPersistentStoreCoordinator:[[self documentManagedObjectContext] persistentStoreCoordinator]];
        }
        return _moc;
    }
     
    - (void)setEmployeeObject:(NSManagedObject *)value
    {
    	// keep only weak ref
    	_emp = value;
    }
     
    - (NSManagedObject *)employeeObject
    {
    	return _emp;
    }
     
    - (NSString *)windowTitleForDocumentDisplayName:(NSString *)displayName
    {
    	return [NSString stringWithFormat:@"Add Employee to '%@'", displayName];
    }
     
    - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)sender
    {
        return [[self managedObjectContext] undoManager];
    }
     
    - (IBAction)addEmployeeToDocumentContext:(id)sender
    {
     
    }
     
    @end

    Notice that we haven’t implemented addEmployeeToDocumentContext: yet.

  • Import your AddEmployeeWindowController.h header file into MyDocument.h and add an attribute for an addEmployeeWindow called _addEmpCtl similar to the existing _addDeptCtl attribute. Declare the IBAction openAddEmployeeWindow: below the existing openAddDepartmentWindow: declaration. Your MyDocument.h should like this:
    #import <cocoa/Cocoa.h>
    #import "AddDepartmentController.h"
    #import "AddEmployeeController.h"
     
    @interface MyDocument : NSPersistentDocument {
     
    	IBOutlet NSArrayController *peopleArrayController;
    	IBOutlet NSArrayController *departmentsArrayController;
     
    	AddDepartmentController *_addDeptCtl;
    	AddEmployeeController *_addEmpCtl;
     
    }
     
    - (IBAction)openPersonWindow:(id)sender;
    - (IBAction)openAddDepartmentWindow:(id)sender;
    - (IBAction)openAddEmployeeWindow:(id)sender;
     
    @end
  • Implement the openAddEmployeeWindow: in MyDocument.m with this code:
    - (IBAction)openAddEmployeeWindow:(id)sender
    {
    	if( _addEmpCtl == nil )
    	{
    		_addEmpCtl = [[AddEmployeeController alloc] initWithDocumentManagedObjectContext:[self managedObjectContext]];
    		[_addEmpCtl setShouldCloseDocument:NO];
    		[self addWindowController:_addEmpCtl];
    	}
    	[_addEmpCtl showWindow:sender];
    }
  • Create a new Window nib file called AddEmployeeWindow.nib and open it in Interface Builder. Set the File’s Owner Class to AddEmployeeController. Set the window outlet of File’s Owner to the Window in the nib file and set the Window delegate to be File’s Owner.
  • Set up the Window in the nib file to look like this:

    The Add New Employee window layout in Interface Builder

  • Connect the Add New Employee button to the File’s Owner addEmployeeToDocumentContext: action.
  • Add an NSArrayController to the nib file called ‘Departments’. Bind its Managed Object Context to the File’s Owner ‘documentManagedObjectContext’. Set its Mode to ‘Entity’ and Entity Name to ‘Department’. Set it to Prepare Content.
  • Bind the popup menu Content to the Departments controller, and the Content Values to the Departments controller ‘name’ model key path.
  • Add an NSObjectController called ‘New Employee’. Set its Mode to Entity and Entity Name to ‘Person’. Bind its Managed Object Context to the File’s Owner managedObjectContext and its Content Object to the File’s Owner ‘employeeObject’ key.
  • Bind the Value of the relevant text fields to the File’s Owner ‘selection’ ‘firstName’ and ‘lastName’ keys.
  • Connect the File’s Owner ‘departmentsPopupButton’ to the Departments popup menu.
  • Now open MyDocument.nib in Interface Builder and connect the Employee ‘+’ button to the File’s Owner openAddEmployeeWindow: action.

At this point it is a good idea to return to Xcode and build and run the project to check the code is working up to this point. Everything should function as expected except for the Add New Employee button having no effect whatsoever. We’ll fix that next! Check that adding a new Department updates an already-open New Employee Window’s popup menu. Check that Undo works as expected.

Code to add the new employee

Finally we need to write the code to add the new employee to the selected department. In AddEmployeeController.m change the addEmployeeToDocumentContext: method to the following:

- (IBAction)addEmployeeToDocumentContext:(id)sender
{
	[[self window] endEditingFor:nil];
 
	NSString *employeeFirstName = [[self employeeObject] valueForKey:@"firstName"];
	NSString *employeeLastName = [[self employeeObject] valueForKey:@"lastName"];
	NSManagedObject *employeeDept = [[departmentsPopupButton selectedItem] representedObject];
 
	NSUndoManager *docUndoMgr = [[self documentManagedObjectContext] undoManager];
	[docUndoMgr beginUndoGrouping];
 
    NSManagedObject *newEmployeeInDocument = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:[self documentManagedObjectContext]];
	[newEmployeeInDocument setValue:employeeFirstName forKey:@"firstName"];
	[newEmployeeInDocument setValue:employeeLastName forKey:@"lastName"];
	[newEmployeeInDocument setValue:employeeDept forKey:@"department"];
 
	[[self documentManagedObjectContext] processPendingChanges];
	[docUndoMgr endUndoGrouping];
 
	NSUndoManager *windowUndoMgr = [[self managedObjectContext] undoManager];
	[windowUndoMgr disableUndoRegistration];
 
	[[self managedObjectContext] deleteObject:[self employeeObject]];
 
	id newEmployeeInWindow = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:[self managedObjectContext]];
 
	[[self managedObjectContext] processPendingChanges];
 
	[windowUndoMgr enableUndoRegistration];
	[self setEmployeeObject:newEmployeeInWindow];
}

This code performs in a similar way to the addDepartmentToDocumentContext: method but this time sets up the new employee with the correct Department from the document object’s managedObjectContext.

Build and run the application to make sure everything works properly. You should be able to Undo changes to the first and last name fields as you tab between them.

Another way to copy the Employee object

The code used above to copy the new object from one context to another is fine for objects that have only a few keys like our Employee object but for objects that have many more keys, writing code to set each value independently can get lengthy and messy. Instead, we can use a dictionary populated with an existing objects values and keys to setup a new object.

Change the addEmployeeToDocumentContext: method to the following:

- (IBAction)addEmployeeToDocumentContext:(id)sender
{
	[[self window] endEditingFor:nil];
 
	NSManagedObject *employeeDept = [[departmentsPopupButton selectedItem] representedObject];
 
	NSUndoManager *docUndoMgr = [[self documentManagedObjectContext] undoManager];
	[docUndoMgr beginUndoGrouping];
 
	NSArray *employeeKeys = [[NSArray alloc] initWithObjects:@"firstName", @"lastName", nil];
	NSDictionary *dictionaryOfEmployeeInfo = [[self employeeObject] dictionaryWithValuesForKeys:employeeKeys];
 
	NSManagedObject *newEmployeeInDocument = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:[self documentManagedObjectContext]];
	[newEmployeeInDocument setValuesForKeysWithDictionary:dictionaryOfEmployeeInfo];
	[newEmployeeInDocument setValue:employeeDept forKey:@"department"];
 
	[[self documentManagedObjectContext] processPendingChanges];
	[docUndoMgr endUndoGrouping];
 
	NSUndoManager *windowUndoMgr = [[self managedObjectContext] undoManager];
	[windowUndoMgr disableUndoRegistration];
 
	[[self managedObjectContext] deleteObject:[self employeeObject]];
 
	id newEmployeeInWindow = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:[self managedObjectContext]];
 
	[[self managedObjectContext] processPendingChanges];
 
	[windowUndoMgr enableUndoRegistration];
	[self setEmployeeObject:newEmployeeInWindow];
 
	[employeeKeys release];
}

Here the code creates a dictionary of keys from the values in the New Window employee object before using that dictionary to populate a new employee object in the main document MOC. We still have to set the department manually.

Note that it would be better practice to implement the code that creates an employeeKeys array as a class array on a custom NSManagedObject subclass for the Person entity. That way, you tie the code for generating the keys to the class itself and stop the Add Employee window from having to know the exact key names of the values that are to be copied. But, since we’re keeping this sample simple and only to demonstrate multiple managed object contexts, we’ll leave it as is for now.

Summary

Well, that’s it for this article on multiple managed object contexts. Again, please let me know if it has been useful to you or if you have ways to make it better!

I’ll be looking at NSTableViews next and how to make a double click in a department/employee table view open a window showing that object’s information.

Share
9 Responses leave one →
  1. Martin Stanley permalink
    March 15, 2009

    I believe that the line:
    • Bind the Value of the relevant text fields to the File’s Owner ’selection’ ‘firstName’ and ‘lastName’ keys.
    should read:
    • Bind the Value of the relevant text fields to the New Employee’s ’selection’ ‘firstName’ and ‘lastName’ keys.

  2. April 26, 2010

    Some techniques for Undo and Window controller management are just brilliant. Thanks for sharing them!

  3. Chris permalink
    November 28, 2010

    Your pages are brilliant. I’m currently working on a similar application, with similar code. One issue I come across is, that when I try to define the instance of my NSManagedObject in my window controller, it gives the error

    “+entityForName: could not locate an NSManagedObjectModel for entity name ‘Part'”

    in the console, and doesn’t open the window. I used the following command;

    Part *aPart = [NSEntityDescription insertNewObjectForEntityForName:@"Part" inManagedObjectContext:tempMoc];

    with Part being the subclass of NSManagedObject. Do I need to pass the NSManagedObjectModel into my initialization method as an argument?

  4. admin permalink*
    November 28, 2010

    There are various things that can cause this error. In answer to your last question, assuming the entity is named “Part”, that’s what you supply when inserting a new object. Don’t try supplying a managed object model directly, or anything like that.

    A few suggestions:

    • Make sure you’re supplying the name of the entity as specified in the .xcdatamodel file, not the name of your custom NSManagedObject subclass if you have one.
    • Make sure the managed object context is not nil by the time this method is called.
    • Log the context’s managedObjectModel to the console using something like NSLog(@"MOM: %@", [tempMoc managedObjectModel]);.
    • Log this managed object model’s list of entities to the console, which will give you a detailed list of the entities it describes, so you can check whether your Part entity is included.
  5. Chris permalink
    November 28, 2010

    Thanks, when I looked at logging tempMoc’s managedObjectModel, I realized that my code never initialized tempMoc. I put an initialization into my getter, and it’s working great now. Thanks for your help.

  6. admin permalink*
    November 29, 2010

    Yes, I get this error most often when using a nil managed object context (by accident…). It’s occasionally because I’ve told the persistent store coordinator to use the wrong model file in an app with multiple data models, hence why my next step is to check the list of entities.

  7. Bill permalink
    January 17, 2011

    The Core Recipies sample NSManagedObject subclass contains this helper method for copying entity attributes into another object context. It seems a bit simpler since it leverages NSEntityDescription.

    /**
    Copies all of the attribute values from the specified object into the
    current object. No class comparison is attempted here (to avoid having to
    walk the inheritance tree), so care should be taken to copy values from an
    object with matching keys.
    */

    – (void) copyAttributesFromObject: (NSManagedObject *)managedObject {

    // get the array of attribute keys and set the items accordingly
    NSArray *attributeKeys = [[self entity] attributeKeys];
    NSString *attributeName;

    // loop through the keys
    int i, count = [attributeKeys count];
    for ( i=0; i<count; i++ ) {

    // Get the attribute name, then copy the value
    attributeName = [attributeKeys objectAtIndex: i];
    [self setPrimitiveValue: [managedObject primitiveValueForKey: attributeName] forKey: attributeName];
    }
    }

Trackbacks and Pingbacks

  1. Blog @ Tim Isted » Blog Archive » Sample project code now available for all posts
  2. Bookmarks about Apps

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS