UndoManager in Swift 5 with example

- Perform undo-redo actions in a more simple way

Updated on 18th April 2019, 10:48 PM GMT 5:30+

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.
  • No, Don’t worry. Follow this article, you will learn everything about UndoManager.

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 found 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 to 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

Thanks for reading this article.

Liked this article?
You can give up to 50 claps: by long pressing the clap button.
if you enjoyed this article or learn something new, please tell your friends and share the love for this article.

@hitendrahckr is typing
Stay tuned, 2nd Part with group undo-redo actions is coming soon…
Follow my
profile so you don’t miss any future article.


Where to go from here?

Read my latest series ‘Design patterns by Tutorials — The power of OOP’

Part-1: Faceted and Fluent Builder pattern in Swift
Part-2: Singleton Design Pattern in Swift

Read another popular series ‘Protocol — The power of Swift’

Part-1: What are type-casting and class-types?
Part-2: Conforming a protocol
Part-3: Protocol composition
Part-4: My rule of thumb for possible data types
Part-5: Protocol as a super-type

Read my latest article Programming Architectures — Redux | ReSwift


Find my repositories on Github
Read my blogs on Medium
Connect me via LinkedIn
Follow me on Twitter