Implementing undo() and redo() in a ViewController loses Undo/Redo action names in the menu

Basically, without implementing these methods everything works fine.

I add undo actions with


        undoManager?.registerUndoWithTarget(self, selector: #selector(ViewController.toggleUndoAddTask(_:)), object: params)
        if !undoManager!.undoing {
            undoManager?.setActionName(kActionNameAddTask)
        }


and the menu titles of the undo/redo actions are then 'Undo kActionNameAddTask' or whatever the name of the action was set.


However, after implementing



    @IBAction func undo(sender: AnyObject) {
        undoManager?.undo()
    }


the menu titles are now simply 'Undo' or 'Redo', i.e. without action names.

If I query undoManager, the undoActionName and the undoMenuItemTitle are both set correctly.


Did I miss something that is preventing these titles from working?

Is your Edit Menu set with autoEnableItems to true ? (attributes Inspector of the Menu, not the Menu Item, or by code)

You're not supposed to implement the undo or redo action methods yourself. Or, if you do, then you take on the responsibility for maintaining the menu items yourself, but I wouldn't recommend you do this.


Can you explain what you're trying to achieve by implementing the action methods? There's probably a better way to do it.

Yes, it is.

Thanks for your reply. It does seem a bit dodgy what I'm trying to do but I haven't been able to find another solution. This is the issue:


If a user undos multiple actions in a quick succession (by pressing command-z), it can happen that while the response from cloud is awaited, the same record gets changed by the next undo action. For example, a record is being updated, but before the cloud response was received, the user action deletes the record in question. The cloud update response will then try to modify a deleted record. This causes issues.


My idea was to prevent users from executing the next undo action until the cloud response was received for the current one (or a time-out has passed).


It seemed that I could do this in validateMenuItem - I could disable tempararily undo menu item. However, in order to have undo menu item pass through the validateMenuItem I had to implement undo and redo methods in the ViewController. That in turn caused the issue with Action names.

Yes, I see the difficulty. But surely you have to deal with the same issue when the user makes two edits (not involving undo or redo) quickly? If you have a mechanism in place to synchronize edits with asynchronous iCloud access generally, can't you extend the same mechanism (or a variant) to handle undo-related transactions too?


The reason I'd recommend this solution rather than trying to disable the undo/redo items is that — assuming that the iCloud transaction might take enough time, on occasions, for the user to notice — the user will see these menu items disabled inexplicably. The implication is that there is nothing to undo or redo, which may lead the user to think that undos have been lost.


In orher words, your intended solution was going to "lie" to the user. It seems to me that it's worth your while to put some effort into not "lying".

Thank you - you've really helped me see this more clearly and hopefully I will come up with a better solution.

Basically, in all the testing I did I only saw this issue be caused by either undo or redo and so I assumed that undo and redo are the cause of the problem. But I was wrong. The issue is, as you said, in managing the asynchronous iCloud access with synchronous user edits. So, the same could happen with most of the user actions.


To try to find a better solution, I will try to locally recreate the record change tagging mechanism that is used in iCloud. That should make it easier to spot when a record was changed locally inbetween iCloud request and response. Then also store all the deleted records for as long as they are likely to be needed.

Implementing undo() and redo() in a ViewController loses Undo/Redo action names in the menu
 
 
Q