Overview
Swift’s concurrency model introduced actors to protect state. In Swift 6, many Cocoa classes—such as UndoManager
—are marked @MainActor
. If you try to call their methods from a synchronous context that isn’t main‑actor isolated, the compiler throws “Call to main actor‑isolated instance method … in a synchronous nonisolated context.” Here’s how to resolve it.
Why This Happens
The Swift concurrency model enforces that any method annotated with @MainActor
runs on the main thread. When your code is not explicitly running under the main actor, calling such methods is disallowed because it risks data races. Apple’s guidance explains that actor isolation prevents concurrent data access; you must either isolate your conformance to the main actor or mark properties/functions as nonisolated
.
Solution 1: Annotate Your Function or Class with @MainActor
If the method logically belongs on the UI thread, add @MainActor
to your class or function declaration. This ensures that calls to UndoManager
happen on the main actor.
@MainActor
func performUndoableAction() {
let undoManager = UndoManager()
undoManager.registerUndo(withTarget: self) { target in
// Undo logic here
}
}
Solution 2: Run the Call Within a Main Actor Context
If you need to call UndoManager
from a background context, wrap the call with await MainActor.run
. This schedules the code on the main actor and respects isolation.
Task.detached {
// background work...
await MainActor.run {
let undoManager = UndoManager()
undoManager.undo() // safe on main actor
}
}
Solution 3: Make Properties Nonisolated (Advanced)
If you have a type that conforms to a protocol requiring nonisolated
properties or methods, declare those members as nonisolated
. This indicates that they’re thread‑safe and can be called from any context.
actor MyUndoManagerWrapper: UndoManagerDelegate {
nonisolated var canUndo: Bool {
UndoManager().canUndo
}
}
Best Practice
- Mark asynchronous functions that update UI or call UIKit/AppKit classes as
@MainActor
. - Use
await MainActor.run { … }
to cross from background threads into the main actor context. - Avoid unnecessary
nonisolated
members; use them only when you’re sure your API is thread‑safe.
Conclusion
Swift’s concurrency model may feel strict at first, but following actor isolation rules helps you avoid race conditions. Isolating your UndoManager
usage to the main actor or explicitly switching actors resolves the “main actor‑isolated instance method” error and keeps your code future‑proof.