# Actors, MainActor, Sendable and Swift 6 Data-Race Safety — Swift

Source: https://www.skillbyai.com/en/swift/c-actors

> Protect shared mutable state with actors and understand Swift 6 checking.

## Data races caught by the compiler

A **data race** occurs when two threads access the same memory at the same time and at least one writes. Swift's answer is **isolation**. An **actor** is a reference type whose mutable state can only be accessed by one task at a time; calls from outside the actor are asynchronous (`await counter.increment()`), and the actor serialises them, so you get thread safety without manual locks. **`@MainActor`** is a global actor representing the main thread: UI code and view models are annotated with it so UI updates always happen on the main thread. **`Sendable`** marks types that are safe to pass between concurrency domains: value types with sendable contents, actors, and immutable or internally synchronised classes. In the **Swift 6 language mode**, the compiler checks isolation and sendability **at compile time** and reports potential data races as errors. Migrating existing code is usually gradual: enable strict concurrency warnings first, annotate types, and fix issues module by module. Later Swift releases add options that make this easier, such as running code on the main actor by default in app targets.

## An actor and a MainActor view model

The actor serialises access; the view model updates UI state on the main actor.

```swift
actor InventoryStore {
    private var stock: [String: Int] = ["PEN-BLUE": 10]

    func reserve(_ sku: String, quantity: Int) -> Bool {
        let available = stock[sku, default: 0]
        guard available >= quantity else { return false }
        stock[sku] = available - quantity               // no other task can interleave here
        return true
    }

    func available(_ sku: String) -> Int { stock[sku, default: 0] }
}

@MainActor
@Observable
final class CheckoutViewModel {
    var message = ""
    private let store: InventoryStore

    init(store: InventoryStore) { self.store = store }

    func buy(_ sku: String, quantity: Int) async {
        let ok = await store.reserve(sku, quantity: quantity)   // hop to the actor and back
        message = ok ? "Reserved \(quantity)" : "Out of stock"   // safe: we are on the main actor
    }
}

struct OrderLine: Sendable {      // value type with sendable properties: safe to share
    let sku: String
    let quantity: Int
}
```

## A bank teller window

An actor is a bank teller who serves one customer at a time through a window. Customers queue (await) instead of all reaching into the cash drawer at once, so the balance is never corrupted.

**Quiz:** What does the Swift 6 language mode add to concurrency?

- [ ] A garbage collector
- [ ] Automatic multithreading of all loops
- [ ] Removal of async/await
- [x] Compile-time checking of isolation and Sendable to prevent data races

*Answer:* Compile-time checking of isolation and Sendable to prevent data races. Swift 6 turns potential data races into compile-time errors.
