SwiftQA Random

How does Swift enforce (or not enforce) thread safety?

The core mechanism: compile-time isolation checking

Swift’s strict concurrency checking works by tracking isolation domains at compile time and requiring proof that data crossing between them is safe. It’s static analysis, not a runtime lock/mutex system.

1. Actors serialize access to their own mutable state — synchronous access to actor-isolated state from outside the actor is a compile error.

2. Sendable gates what can cross an isolation boundary — any value passed between isolation domains must conform to Sendable.

class Counter { var value = 0 }  // not Sendable — no synchronization

func launch(_ counter: Counter) {
    Task {
        counter.value += 1  // ❌ error: Counter is not Sendable
    }
}

3. sending as the finer-grained escape valve — lets a specific non-Sendable value cross a boundary once, if provably in a disconnected region.

What this actually prevents

Genuine low-level data races — two threads concurrently reading/writing the same memory without synchronization. If Swift 6 strict mode compiles clean, you provably don’t have this class of bug.

What it does NOT prevent

1. Reentrancy bugs — an actor’s isolation guarantees “only one task runs isolated code at a time,” but suspension points (await) are reentrancy windows.

2. Escape hatches that opt out entirely

  • @unchecked Sendable — asserts safety yourself, unverified by the compiler.
  • nonisolated(unsafe) — bypasses actor isolation checks at the property level.

3. C/Objective-C interop and unsafe pointers — no Swift-visible isolation information at all.

4. Priority inversion, deadlocks, and performance issues — strict concurrency checking is about correctness, not liveness.

5. Logic errors around ordering and state machines generally.

The precise claim to make in an interview

Swift’s concurrency model provides compile-time-enforced freedom from data races — not freedom from concurrency bugs in general.

One-liner if pressed: “Swift enforces data-race freedom at compile time via actor isolation and Sendable checking — if it compiles under Swift 6 strict concurrency, you provably don’t have unsynchronized memory access across threads — but it explicitly does not, and cannot, catch reentrancy bugs, deadlocks, or anything hidden behind @unchecked Sendable or C interop, since those are logic or opt-out problems, not aliasing problems.”