Adding Undo and Redo support to iOS

People make mistakes. Let’s use allow undoing.

You have accidentally shaken your phone and noticed that little alert screen that says “Undo [something]”. In this story we are going to implement Undo and Redo actions in an iOS app.

By now you are making all your screens using a declarative and functional approach, right? 😉 If not, you can read all about it. Expanding on that approach, we can easily implement undo actions into our app if all our View Controller state is encapsulated like it was demostrated in that article.

Looking at the documentation

When I looked at Undo Manager it appeared like I was going to be dealing with Selectors, old Objective-C API, and whatever else was lurking underneath. I tried to get around this by creating a wrapper struct with some syntactic sugar.

struct Undoer {
let undoManager: UndoManager
...
}

Thankfully, Geoffrey Foster talked me out of it and I end up choosing a better approach. By simply extending UndoManager to include one more function. Now, taking everything I had in the previous wrapper, all I was left with was this.

Pretty simple right?

Pretty simple right? To be honest, the biggest issue with writing this short function was embracing the fact that you want to capture your variables inside the registerUndo closure, while also having it call itself.

Exploring the code

UndoManager works exactly like that. It retains a copy of the data in the closure so you can revert any changes by calling undoManager.undo() or redo() for that matter. Doing so, executes our handler callback and recursively calls itself again, but this time with the values inverted. Creating that loop of undo and redo actions that we desire.

Fixing the bug 🙃

There is one issue that you probably spotted by now. If we use reference types, we won’t be maintaining the previous data. Thankfully that is why we have the Codable constraint in the generic type T.

By leveraging Codable, we can implement a quick and dirty version of copy to serialize and deserialze the content of the data, so we can maintain the state.

Don’t @ me 🙈

This is more like it, now our UndoManager can store and restore the state of any class or struct, and we barely even had to break a sweat writing this.

You can find the source code of this function with a quick implementation into a View Controller in the following link.

Did I miss anything? Get in touch on Twitter: @raulriera