How to Implement @MainActor Annotations for Swift 6.2 Concurrency Compliance

To implement @MainActor annotations for Swift 6.2 concurrency compliance, annotate UI-bound classes and structs with @MainActor, isolate protocol conformances using @MainActor extensions, and protect global state by marking singletons with the same attribute.

Swift 6.2 introduces Approachable Concurrency, where code runs on a single thread by default and parallelism requires explicit opt-in. According to the Dimillian/Skills repository, properly applying @MainActor annotations eliminates data-race diagnostics and satisfies the new compiler checks for UI-bound code.

Why @MainActor Is Required in Swift 6.2

Swift 6.2 assumes UI-related types run on the main actor, but the compiler cannot verify this without explicit annotations. When you omit @MainActor from types that manipulate UI state, the compiler generates warnings about potential data races. The repository's concurrency reference material in swift-concurrency-expert/references/swift-6-2-concurrency.md identifies three critical scenarios requiring @MainActor:

  • Mutable UI State: Classes owning @Published properties or view state trigger data-race warnings without isolation
  • Protocol Conformances: Implementing protocols on main-actor types raises "conformance crosses into main-actor-isolated code" errors unless the conformance is scoped with @MainActor
  • Global State: Static properties accessed from UI code produce "not concurrency-safe" warnings unless protected by the main actor

The core language changes and initial examples appear at lines 14-16 of the concurrency reference file, while isolated protocol conformances are demonstrated at lines 84-88.

Step-by-Step Implementation Guide

Identify UI-Bound Types

First, locate any UIViewController, NSView, SwiftUI View, or ObservableObject view model that mutates UI state. These candidates must run on the main thread to avoid data races.

Annotate the Type Declaration

Apply @MainActor to the class or struct definition to ensure all stored properties and methods run on the main thread. The SKILL.md file at lines 40-56 provides a concrete before-and-after workflow:

@MainActor
class MyViewModel: ObservableObject {
    @Published var title: String = ""
    func load() { title = "Loaded" }
}

This annotation tells the compiler that every method and property access occurs on the main actor, eliminating isolation warnings.

Handle Protocol Conformances

When a main-actor type conforms to a protocol, move the conformance to an isolated extension. In swift-6-2-concurrency.md at lines 84-88, the repository demonstrates scoping the conformance:

@MainActor
extension MyViewModel: Exportable {
    func export() { /* safe UI-thread work */ }
}

This pattern resolves the "conformance crosses into main-actor-isolated code" error by explicitly marking the protocol implementation as main-actor-isolated.

Protect Global and Static State

Mark global properties or singletons with @MainActor to ensure thread-safe access from UI code:

@MainActor
final class Settings {
    static let shared = Settings()
    var currentTheme: Theme = .light
}

This approach satisfies the compiler's requirement that static state accessed from UI code must be concurrency-safe.

Verify Compiler Diagnostics

Rebuild the project after applying annotations. All data-race diagnostics related to these symbols should disappear. Run the test suite to confirm that runtime behavior remains unchanged while gaining compile-time safety guarantees.

Practical Code Examples

Basic UI-Bound Class

Convert a standard view model to use main-actor isolation:

// Before: data-race warning
class PhotoPickerViewModel: ObservableObject {
    @Published var selectedImage: UIImage?
    func select(_ img: UIImage) { selectedImage = img }
}

// After: main-actor isolation
@MainActor
class PhotoPickerViewModel: ObservableObject {
    @Published var selectedImage: UIImage?
    func select(_ img: UIImage) { selectedImage = img }
}

Isolated Protocol Conformance

Fix protocol conformance errors by isolating the extension:

protocol Exportable {
    func export()
}

// Before: compiler error – conformance crosses actor boundary
@MainActor
class StickerModel: Exportable {
    func export() { /* uses main-actor state */ }
}

// After: isolated conformance
@MainActor
extension StickerModel: Exportable {
    func export() { /* safe */ }
}

Global Singleton Protection

Secure shared state using @MainActor:

// Before: "static property … is not concurrency-safe"
final class ThemeManager {
    static let shared = ThemeManager()
    var currentTheme: Theme = .light
}

// After: main-actor protection
@MainActor
final class ThemeManager {
    static let shared = ThemeManager()
    var currentTheme: Theme = .light
}

Combining Background Work with Main Actor

Use @concurrent for background tasks while keeping UI updates on the main actor:

// Heavy work runs off-the-main-actor using @concurrent (Swift 6.2+)
nonisolated struct ImageProcessor {
    @concurrent
    func heavyCompute(_ data: Data) async -> ProcessedImage { 
        // CPU-intensive work
        return ProcessedImage()
    }
}

// UI code stays on the main actor
@MainActor
func processImage(_ data: Data) async {
    let result = await ImageProcessor().heavyCompute(data)
    // update UI safely here
}

Common Pitfalls to Avoid

  • Over-annotating: Applying @MainActor to pure data models that never touch UI causes unnecessary thread hops and performance degradation
  • Missing isolated conformance: Forgetting to scope a protocol conformance with @MainActor still raises the "crosses into main-actor-isolated code" error
  • Confusing @concurrent with @MainActor: The @concurrent attribute marks code for explicit background execution and does not provide main-actor isolation

Key Source Files in Dimillian/Skills

The repository contains reference material demonstrating these patterns:

Summary

  • Annotate UI-bound types with @MainActor to eliminate data-race warnings in Swift 6.2
  • Isolate protocol conformances using @MainActor extensions to resolve crossing-isolation errors
  • Protect global state by marking singletons and static properties with @MainActor
  • Reserve @concurrent for explicit background work, not UI code
  • Reference Dimillian/Skills files swift-6-2-concurrency.md and SKILL.md for authoritative examples at specific line numbers

Frequently Asked Questions

What happens if I omit @MainActor on a UI-bound class in Swift 6.2?

The compiler generates data-race warnings because Swift 6.2 assumes UI code runs on the main actor but cannot prove isolation without the annotation. Mutable state accessed from multiple threads triggers these diagnostics to prevent runtime crashes.

How do I fix "conformance crosses into main-actor-isolated code" errors?

Move the protocol conformance to an isolated extension. Instead of declaring @MainActor class MyClass: Protocol, write @MainActor extension MyClass: Protocol { }. This tells the compiler that the protocol implementation runs on the main actor.

Can I use @concurrent instead of @MainActor for UI code?

No. The @concurrent attribute marks code for explicit background execution and does not provide main-actor isolation. UI code must use @MainActor to ensure it runs on the main thread, while @concurrent is reserved for CPU-intensive or I/O work that should run in parallel.

Where can I find practical examples of @MainActor implementation?

The Dimillian/Skills repository provides authoritative examples in swift-concurrency-expert/references/swift-6-2-concurrency.md (lines 14-16 for basic usage, lines 84-88 for protocol conformances) and swift-concurrency-expert/SKILL.md (lines 40-56 for implementation workflows). The swiftui-ui-patterns/references/ directory contains production-ready SwiftUI code using these annotations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →