23
            May
            2023
        
                            
    Undo/Redo with SwiftUI TextField
Reading time: 1 min
This recipe shows how to implement undo and redo with SwiftUI TextField.
The end result looks like this:

This solution works for SwiftUI 2+ (iOS 14+, macOS 11+).
Here's the full recipe:
- You can access the view's UndoManager from the environment, using the undoManagerkey.
- The UndoManager'sregisterUndomethod requires a target that needs to be a class, so we'll use a separate@StateObjectto hold this info. If you're following MVVM, the View's ViewModel is a perfect place for that.
- In the TextFielditself, pass a custom Binding that interacts with theUndoManagerwhen the text is changed.
Check out the code for more details:
class ViewModel: ObservableObject {
  @Published var text = ""
  func registerUndo(_ newValue: String, in undoManager: UndoManager?) {
    let oldValue = text
    undoManager?.registerUndo(withTarget: self) { [weak undoManager] target in
      target.text = oldValue // registers an undo operation to revert to old text
      target.registerUndo(oldValue, in: undoManager) // this makes redo possible
    }
    text = newValue // update the actual value
  }
}
struct UndoTest: View {
  @Environment(\.undoManager) var undoManager
  @StateObject private var model = ViewModel()
  var body: some View {
    VStack(spacing: 20) {
      TextField("Undo/redo test", text: Binding<String>(
        get: {
          model.text // retrieve the value
        }, set: {
          model.registerUndo($0, in: undoManager) // set the value
        }))
      Button("Undo") {
        undoManager?.undo()
      }
      .disabled(undoManager?.canUndo == false)
      Button("Redo") {
        undoManager?.redo()
      }
      .disabled(undoManager?.canRedo == false)
    }
    .padding()
  }
}