Skip to content

Command ​

Command represents an action as an object or value. This is useful when an app must store, queue, retry, log, or undo user actions.

When it helps in iOS ​

In a checklist editor, users may rename an item and immediately tap Undo. A command can retain the old and new values, so undo does not depend on guessing the previous state later.

Example: an undoable rename ​

swift
import Foundation

final class Checklist {
    var titles: [UUID: String] = [:]
}

protocol EditingCommand {
    func execute()
    func undo()
}

final class RenameItem: EditingCommand {
    private let checklist: Checklist
    private let itemID: UUID
    private let newTitle: String
    private var previousTitle: String?

    init(checklist: Checklist, itemID: UUID, newTitle: String) {
        self.checklist = checklist
        self.itemID = itemID
        self.newTitle = newTitle
    }

    func execute() {
        previousTitle = checklist.titles[itemID]
        checklist.titles[itemID] = newTitle
    }

    func undo() {
        checklist.titles[itemID] = previousTitle
    }
}

let checklist = Checklist()
let id = UUID()
checklist.titles[id] = "Pack charger"
let rename = RenameItem(checklist: checklist, itemID: id, newTitle: "Pack USB-C charger")
rename.execute()
rename.undo()

The command owns enough information to reverse this one change. Store executed commands on an undo stack, or register undo() with UndoManager in a UIKit editor.

How to use it in a real app ​

Execute the command through the editor's model, update the visible list, and add it to the undo history only after success. If edits are synced to a server, undo becomes another server operation; handle conflicts and failed sync explicitly.

Watch for: This simple command assumes no other edit changes the same title between execute and undo. For collaborative editing, use version checks or an operation log.

Trace undo ​

execute() captures the previous title before writing the new title. undo() restores that captured value. A command history should only record a command after execution succeeds, and a new edit after undo normally clears the redo stack.

For persistent or collaborative edits, a command must include an item version and may need a server acknowledgment. Compare Command with Memento: Command records what changed, while Memento records a whole snapshot. Test execute, undo, redo, missing items, and interleaved edits before wiring commands to UndoManager.

The action is data plus behavior ​

RenameItem stores the target ID, requested title, and previous title. execute() reads the current title immediately before changing it, which makes undo() able to restore the exact earlier value. A history controller could hold an array of EditingCommand values without knowing the details of renaming. The command is an object because it retains the captured previous state between execution and undo.

From example to an iOS editor ​

A text-field commit creates and executes the command, then registers an undo action with UndoManager. The view reads the updated checklist through observation or a diffable snapshot. For redo, either execute the command again with carefully defined semantics or store a separate redo operation; the simple sample should not be reused blindly because a second execute() would overwrite previousTitle. For a network-synced checklist, wait for server acceptance or represent pending edits explicitly.

Failure modes and verification ​

Test missing IDs, two renames of the same item, undo order, and whether a failed persistence write still enters history. Commands describe changes; a Memento captures a snapshot. Use commands when operations need queueing, auditing, or granular undo. For one transient button action with no history, a method call is simpler.

Next: Coordinator →

Released under the MIT License.