iphonecore-datansundomanager

Is it OK to use multiple NSUndoManagers with one Core-Data managedObjectContext?


//Edit: Really, nobody has any suggestions or thoughts on this? Have I asked the question wrongly somehow?//

My iPhone app has a single managedObjectContext with a moderately complicated data model. I'm now adding undo functionality, and am not clear on how best to handle nested viewControllers (as each layer might modify the data model).

Apple's docs point out: "Consider an application that displays a list of books, and allows you to navigate to a detail view that in turn allows you to edit individual properties of the book (such as its title, author, and copyright date). You might create a new book from the list screen, navigate between two other screens to edit its properties, then navigate back to the original list. It might seem peculiar if an undo operation in the list view undid a change to the author’s name that was made two screens away rather than deleting the entire book."


So what's the best way to implement this? Currently, I'm thinking to have each viewController keep its own undoManager, which would be active whenever it's on the screen. So my understanding is that this would require the following steps (for each VC):

In addition, I have to remain firstResponder:

So far, that seems like it works, even across load/unload cycles, and is nicely self-contained, but I have several questions:


Solution

  • Each view controller can have its own undo manager. A controller should only be responsible for the fields that it directly changes. Once you back out of the corresponding view, the controller should be released and the undo manager with it.

    Let's say you have 3 levels. Level 1 represents the entire record, level 2 represents a subset of data from level 1, and level 3 represents a subset of data from level 2.

    Once you back out of level 3, you've basically said I accept and you shouldn't need to undo any of that data in level 2. This changed data should only show up as read-only data in level 2 if it shows at all. Similarly, once you back out of level 2, you should release its undo manager.

    Back in level 1, since it represents the entire record, why not have a Cancel button instead of trying to undo (or in addition to, depending on what your level 1 controller does)?

    Then, if you want to cancel the entire operations you can send a message like the following to your managed object context:

    [myMOC refreshObject:theEditedObject mergeChanges:NO];
    

    This will effectively roll back the entire record.

    If, for whatever reason, you decide to keep level 3's undo manager around while you're in level 2 and you do a rollback at level 2, only the data pertaining to level 2's undo manager would be rolled back. Level 3's undo manager is separate and Core Data does not see the undo managers as nested.

    A managed object context can't get confused due to multiple undo managers because it can only keep track of one at a time via its setUndoManager: method.

    You probably won't need to use processPendingChanges unless somehow a rollback is taking place before the completion of the event loop after a change has been made. I wouldn't worry about this unless your undo only restores some of the data that is supposed to be recorded with the undo manager up to that point.