Swift Academy · Lesson

Sendable and thread-safety checking

Mark data as Sendable , write @Sendable closures, and choose between value types, actors, and @unchecked Sendable for thread-safe sharing.

Lesson 3 of 38 steps

Sendable and thread-safety checking is a free Swift Academy lesson on CoddyKit. This is lesson 3 of 3. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, and your progress syncs across the web and the CoddyKit app. The Swift Academy course includes 3 lessons in total.

What is Sendable?

Sendable is Swift’s way to ensure data is safe to pass between concurrent tasks/actors.

  • Most structs/enums are Sendable automatically.
  • Classes are not Sendable by default.
  • Use actors or @unchecked Sendable (rare, with manual safety).

Value types are easy

Structs with Sendable members are Sendable; passing them between tasks is safe.

// Value types with value-only stored properties are Sendable.
// They are safe to transfer across tasks.
struct Point: Sendable { let x: Int; let y: Int }

func shift(_ p: Point) -> Point { Point(x: p.x + 1, y: p.y + 1) }

// Use in parallel tasks
Task {
    let p = Point(x: 1, y: 2)
    async let a = shift(p)
    async let b = shift(p)
    let (p1, p2) = await (a, b)
    print(p1, p2) // Point(x: 2, y: 3) Point(x: 2, y: 3)
}

Classes: use actors

Actors make reference semantics safe across tasks by serializing access.

// Reference types (class) are not Sendable by default.
// Prefer actors to guard mutable state.
actor SafeCounter {
    private var value = 0
    func inc() { value += 1 }
    func read() -> Int { value }
}

let counter = SafeCounter()
Task {
    async let t1 = counter.inc()
    async let t2 = counter.inc()
    _ = await (t1, t2)
    print(await counter.read()) // 2
}

// (Alternative) A plain class would need locks + @unchecked Sendable; see later.

@Sendable closures

Mark closures @Sendable when they may run on other executors. Avoid capturing non-Sendable mutable state.

// Some APIs require closures to be @Sendable so captured values are safe.
func doTwice(_ f: @Sendable () -> Int) -> Int { f() + f() }

let base = 10
// Capturing an immutable value (let) is fine for @Sendable closures.
let result = doTwice { base + 1 }
print(result) // 22

// Detached tasks also use @Sendable under the hood:
let t = Task.detached { () -> Int in
    // Do not capture non-Sendable mutable state here.
    return 5 * 5
}
Task { print(await t.value) }

@unchecked Sendable (advanced)

@unchecked Sendable opts out of compiler checks. Only use with strict internal synchronization (locks/queues) and as a last resort.

import Foundation

// Only when you KNOW it is safe: wrap with a lock and mark @unchecked Sendable.
final class Box<T>: @unchecked Sendable {
    private var value: T
    private let lock = NSLock()

    init(_ value: T) { self.value = value }

    func read() -> T {
        lock.lock(); defer { lock.unlock() }
        return value
    }

    func write(_ newValue: T) {
        lock.lock(); value = newValue; lock.unlock()
    }
}

let shared = Box<Int>(0)
let a = Task.detached { shared.write(1) }
let b = Task.detached { shared.write(2) }
Task {
    _ = await (a.value, b.value)
    print(shared.read()) // 1 or 2 (last writer wins, but no data race)
}

Best practices

Guidelines:

  • Prefer value types (Sendable-friendly).
  • Use actors for shared mutable state.
  • Write @Sendable closures; avoid capturing non-Sendable mutables.
  • Only use @unchecked Sendable with rigorous synchronization.

Meaning of Sendable

Quick check: What does Sendable promise?

Recap

Recap: Use Sendable to model cross-task safety, prefer actors or value types, write @Sendable closures, and reserve @unchecked Sendable for well-synchronized internals only.

Free to start

Learn Swift with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
122
Lessons
409

Frequently Asked Questions

Is the “Sendable and thread-safety checking” lesson free?

Yes — any 3 lessons of the Swift Academy learning path, including “Sendable and thread-safety checking”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Swift Academy course includes 3 lessons in total.

What will I learn in “Sendable and thread-safety checking”?

Mark data as Sendable , write @Sendable closures, and choose between value types, actors, and @unchecked Sendable for thread-safe sharing. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Swift Academy?

No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 3.

How long does the “Sendable and thread-safety checking” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Swift Academy lesson?

Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. TaskGroup for parallelism
  2. Actors & data isolation, nonisolated
  3. Sendable and thread-safety checking
← Back to Swift Academy