3

I'm working on an app where user can edit some entries manually, but they can be updated from server as well.

For user updates I'm using UndoManager to allow user to cancel / confirm group of changes. When user enters edit mode I'm doing:

//when edit mode is entered
[[self.contact.managedObjectContext undoManager] beginUndoGrouping];
//when edit mode is finished
[[self.contact.managedObjectContext undoManager] endUndoGrouping];

//depending on button that was pressed to end edit mode, either:
[[self.contact.managedObjectContext undoManager] undo];
//or
[[self.contact managedObjectContext] save:nil];

For automatic updates from server (object can be changed by server) I'm using child-parent MOC. The one above which I'm using to manage edit mode is a parent one. I'm creating a child MOC and in background pull changes from server. At the end of process I'm doing:

 [self.moc.save:nil]; //save to local MOC
 [self.moc.parentContext.save:nil]; //save to main thread MOC (the one with undo)

My problem is that currently if changes are coming while user is editing and user decides to do UNDO - all changes that were automatically pulled are lost as well.

As a workaround I thought about:

  1. blocking auto-updates when user is in edit mode
  2. blocking edit mode when auto-update is in progress

But it sounds like a lot of work and potential bugs. Maybe there is a way to solve my problem on layer of MOCs, so user actions can be separated from automatic saves.

Grzegorz Krukowski
  • 18,081
  • 5
  • 50
  • 71

2 Answers2

0

I don't know if you were just cutting corners, but you need to be careful where you save your contexts. Make sure you do that on the right thread.

I'm guessing you have a private queue child and a main queue parent. Use performBlock... for saving each in turn.

As pointed out by others, when saving the child, you should deregister the main undo manager to prevent it generating an undo for the changes from the child.

Merging with undo managers can get very messy, and lead to ugly problems with deleted objects and the like. I generally just err on the side of safety, and completely clear the undo manager after such a merge with removeAllActions.

Drew McCormack
  • 3,490
  • 1
  • 19
  • 23
  • Thanks for pointing out. I cannot remove all undo actions, I need edit mode to have undo, but I've managed to make it work with disableUndoRegistration. I have some other problem right now, but it's coming out of my merge logic. With centralized context management code, I don't find it too messy. – Grzegorz Krukowski Feb 09 '14 at 17:56
0

You've got things a bit muddled.

For user updates that you would like to be atomic, use parent-child contexts. The advantage is any changes made to the child context can be thrown away if necessary, e.g. if tapping cancel. Both are main queue contexts obviously. Never use a child context in the background (i.e. with a private queue) they are not designed for that, if you do so it will just make your main thread hang even longer when the save merges multiple changes into the main thread context.

For your background server updates you should use a private queue context with a new persistent store coordinator or use the convenience method persistentContainer.newBackgroundContext. See Apple's Earthquakes sample.

malhal
  • 26,330
  • 7
  • 115
  • 133