Swiftpack.co -  aacapella/swift-composable-undo as Swift Package
Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
A library that provides undo semantics for the Composable Architecture with optional bridging tofUndoManager.
.package(url: "https://github.com/aacapella/swift-composable-undo.git", from: "v0.1.0")

Swift Composable Undo

A library that provides undo semantics for the Composable Architecture with optional bridging with UndoManager.


It is hard to use UndoManager API with state that uses value sematics. Furthermore, the instance provided by SwiftUI may not be available in all contexts. There have been previous discussions which resulted in solutions that were not generic. This library provides a way to scope Undo/Redo operations within a View with minimal efforts.


The workspace ComposableUndo contains the TicTacToe demo from ComposableArchitecture repository. Open the GameCore.swift file to see how Composable Undo is integrated.

Basic Usage

To use ComposableUndo in your project, you need to annotate the state fields that needs undo tracking:

struct Person: Equatable {
  var firstName: String
  var lastName: String
  var phoneNumber: String

struct AppState: Equatable {
  @CheckpointState var person: Person

You also need to add checkpoint action to your domain. It is advisable to confirm the domain action enum to SingleCheckpointAction protocol when only one of the states fields is annotated:

enum AppAction: Equatable, SingleCheckpointAction {
  case checkpoint(CheckpointAction)
  // Your domain's other actions:

If you are planning to use UndoManager, it must be registered using view lifecycle methods:

struct AppView: View {
  @Environment(\.undoManager) var undoManager
  // Store declarations
  var body: some View {
    WithViewStore(store) { viewStore in
      SomeView {
      .onAppear { viewStore.send(.checkpoint(.attachManager(undoManager))) }
      .onDisappear { viewStore.send(.checkpoint(.detachManager)) }

If this view's state is computed by a super view, make sure that the detach happens before clearing the state. See NewGameCore.swift in the example on how to do this.

With this initial setup out of the way, each state change that can be undone must be registered by returning effect from the reducer:

case .updatePerson:
 // ... code to update state
 return .checkpoint(.register("Change person"))

Note that the register string is passed on to UndoManager and appears in the menu on macOS.


Stars: 6
Last commit: 3 days ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Submit a free job ad (while I'm testing this). The analytics numbers for this website are here.

Release Notes

6 days ago

Basic undo/redo functionality

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics