UndoManager in Swift 5 with example

Perform undo-redo actions in a more simple way

Hitendra Solanki
5 min readApr 18, 2019

Updated on 29th April 2021, 10:15 AM GMT 5:30+

Check out my Card Saver to save your Credit and Debit cards securely.
- Developed in SwiftUI
- only [<1MB] in Size
https://apps.apple.com/US/app/id1565309672?l=en

Have you heard about the UndoManager or NSUndoManager before? If your answer is,

  • Yes, That’s great. You are one of very few developers who know about UndoManager, Let’s explore it in details together.
  • No, Don’t worry. Follow this article, you will learn everything about UndoManager.

Let’s understand the UndoManager,

  1. What is UndoManager?
  2. What are the benefits of UndoManager?
  3. Understand UndoManager with a real-life example.
  4. How does undo and redo works in UndoManager with almost zero efforts?
  5. Download full-example from Github link available at the bottom.

I found that many of the developers required to implement undo/redo actions during development, usually in photo editing or video editing applications.
Suppose we are developing a photo editing application with Undo-Redo functionality in the editing screen. Consider that the user applied three filters on the existing image.

  1. Sepia effect filter
  2. Radial blur effect filter
  3. Dark light effect filter

Undo action will remove the last applied filter “Dark light effect filter”. Redo will again apply the last removed filter by undoing the action.

How will you implement this?

I have seen that many of the developers who are not aware of UndoManager implement this using array/stack. Implementation will result in complex conditional code and too much data overload, we need to keep the copy of the image after any filter applied.

We can solve this data overload and conditional complexity issue by using UndoManager easily.

What is UndoManager?

UndoManager/NSUndoManager are the classes that provide the undo-redo operations in a more easy, simple and convenient way. NSUndoManager is implemented in Object-C in Foundation Framework. UndoManager is the Swift implementation of NSUndoManager.

What are the benefits of UndoManager?

  • We can perform undo-redo actions easily.
  • We don’t need to maintain our own stack/array that usually ends up with the complex conditional logic for undo and redo actions.

Let’s understand UndoManager with below example,

We have only one screen having a list of students in table view and 3 buttons.

  1. Add button will add a new student in that list.
  2. Undo button will remove the last added student.
  3. Redo button will add the student, removed by undoing the action.

Student.swift,

We have two properties name and studyInClass of type string.
We have one more static constant property allStudentswhich holds the predefined students. We can consider allStudents as a tiny database of our application, which can be accessed via Student.allStudents.

StudentTableViewCell.swift

StudentTableViewCell is a simple custom class to represent student cell in the list. This class has a data property named‘student’, which is used to update cell UI.

StudentsListViewController.swift

In StudentsListViewController.swift,
line 23, We have property students of array type having one student instance when the screen loads.
We have divided our view controller StudentsListViewController in to multiple extensions to make it more readable.

StudentsListViewController.swift — First extension from line 34,
This extension will only handle IBActions triggered by User and enable/disable buttons.

  • btnAddNewStudentDidTap(_ :) will add a new student in the list and register its inverse action for undo.
  • btnUndoDidTap(_ :) will perform the undo operation
  • btnRedoDidTap(_ :) will perform the redo operation.
  • enableDisbaleActionsButtons() will enable the UI buttons if respective actions are possible. Add button will be disabled once we will have ten students on the list because we have only ten predefined student objects in our tiny database.

StudentsListViewController.swift — Second extension from line 62,
This extension will add a new student to list and remove a student from the list at a specific index.

StudentsListViewController.swift — Third extension from line 78,
This extension registers our undo and redo actions.

  • Everything is dependent on the index when the current operation is performed.

You need to understand two important points here,
- Undo: inverse actions of the performed action
- Redo: inverse of the inverse of performed action, i.e. Undo of Undo is the Redo

  • When you perform any action, you need to register to undo action using the method registerUndo(withTarget:,handler:) on the instance of the class UndoManager.
    Every view controller has one undoManager instance by default.
  • In our above methods, we have used the default undoManager instance of the view controller to register for undo and redo(undo of undo) actions on line number 83 and 90.
  • Method addNewStudentUndoActionRegister(at:) used to register to undo the action of addNewStudent(at:) operation. This method performs the undo of addNewStudent(at:)means this will remove the last added student during undo operation.
  • Method removeNewStudentUndoActionRegister(at:) used to register to undo the action of removeNewStudent(at:) operation. Undo of removeNewStudent(at:) operation is the redo of addNewStudent(at:) operation. Undo of Undo is Redo.

StudentsListViewController.swift — the Fourth extension from line 97,
This extension handles the table view related methods. Here we are using only two essential methods to create a table view in order to reduce complexity.

Download the full project from this Github.

Exercise

Add swipe to delete actions in table view cell, implement undo-redo actions for swipe to delete.

We need to understand how action performs by UndoManager.

When you call self.undoManager?.undo(), UndoManager triggers the block of undoing action registered using registerUndo(withTarget: self, handler:) recently.

When you call self.undoManager?.redo(), UndoManager triggers the block of undoing action registered using registerUndo(withTarget: self, handler:) recently inside the last triggered undo block.

Conclusion:

  • Last registered action is treated as the latest Undo action
  • Last registered action within the last triggered undo action is treated as the latest Redo action

Liked this article?
Give claps and show your support. It doesn’t cost you anything to clap.

--

--

Hitendra Solanki

Software Engineer, {Self-taught Developer by passion, Quick Learner, Mentor, blogger, TeamLead} GitHub: https://github.com/hitendradeveloper