# How to Implement @MainActor Annotations for Swift 6.2 Concurrency Compliance

> Learn to implement @MainActor annotations in Swift 6.2 for concurrency compliance. Annotate UI classes, isolate protocol conformances, and protect global state effectively.

- Repository: [Thomas Ricouard/Skills](https://github.com/Dimillian/Skills)
- Tags: how-to-guide
- Published: 2026-04-01

---

**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](https://github.com/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`](https://github.com/Dimillian/Skills/blob/main/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`](https://github.com/Dimillian/Skills/blob/main/SKILL.md) file at lines 40-56 provides a concrete before-and-after workflow:

```swift
@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`](https://github.com/Dimillian/Skills/blob/main/swift-6-2-concurrency.md) at lines 84-88, the repository demonstrates scoping the conformance:

```swift
@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:

```swift
@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:

```swift
// 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:

```swift
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`:

```swift
// 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:

```swift
// 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:

- **[`swift-concurrency-expert/references/swift-6-2-concurrency.md`](https://github.com/Dimillian/Skills/blob/main/swift-concurrency-expert/references/swift-6-2-concurrency.md)**: Core Swift 6.2 concurrency changes, `@MainActor` usage examples, isolated conformances, and global state handling at lines 14-16 and 84-88
- **[`swift-concurrency-expert/SKILL.md`](https://github.com/Dimillian/Skills/blob/main/swift-concurrency-expert/SKILL.md)**: High-level workflow for adding `@MainActor` with before/after implementation snippets at lines 40-56
- **`swiftui-ui-patterns/references/`**: Real-world SwiftUI patterns already using `@MainActor` in production contexts
- **[`macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/main.swift`](https://github.com/Dimillian/Skills/blob/main/macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/main.swift)**: Minimal SwiftUI app entry point showing `@main` structure compatible with `@MainActor` isolation

## 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`](https://github.com/Dimillian/Skills/blob/main/swift-6-2-concurrency.md) and [`SKILL.md`](https://github.com/Dimillian/Skills/blob/main/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`](https://github.com/Dimillian/Skills/blob/main/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`](https://github.com/Dimillian/Skills/blob/main/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.