swift · note · concurrency

MainActor: What To Mark and Why

Published August 6, 2026 · 2 min read · intermediate

Context

@MainActor is useful when UI-facing work must stay on the main thread, but it is easy to overuse it. If everything becomes main-actor isolated, you lose the ability to separate UI work from real background work.

The snippet

@MainActor
final class SettingsViewModel: ObservableObject {
    @Published var isLoading = false

    func load() async {
        isLoading = true
        defer { isLoading = false }
    }
}

Why this works

The annotation tells Swift concurrency that this type belongs to UI work. That makes state updates safe for SwiftUI and keeps the intent obvious to readers. It also prevents accidental updates from a background task that should have been isolated elsewhere.

Use it when:

  • the type drives visible UI state
  • the work must coordinate with SwiftUI updates
  • the object is meant to be called from view code

Avoid it when:

  • the type does heavy background processing
  • the code should stay isolated from UI concerns
  • the annotation would force unrelated work onto the main thread

Takeaway: Mark the boundary that owns UI state, not every piece of code that touches data.

Next steps