Sendable — What It Is, Thread Safety Protocol

Author: IT Sectr Published: 2026-06-18 Reading time: 10 min

Sendable is a Swift protocol that marks types safe for passing between threads. When working with concurrency (actor, async/await, Task), the Swift compiler requires that all data passed between isolated contexts conform to Sendable. This prevents accidental passing of unsafe types that could lead to data races. The protocol acts as a contract: a type conforming to Sendable guarantees the absence of internal unsynchronized state. According to WWDC 2021, the Sendable protocol is a mandatory element when designing a safe multithreaded architecture.

Key Takeaways

  • Sendable — a protocol that guarantees no data races when passing between threads
  • Value types (struct, enum) automatically conform to Sendable if all their properties are Sendable
  • @unchecked Sendable — a bypass mechanism for classes whose safety the developer takes responsibility for
  • Sendable closures are marked with the @Sendable attribute, which checks variable capture
  • The compiler gives an error if a non-Sendable type is passed between isolated contexts

What Is the Sendable Protocol in Swift?

Sendable is a protocol from the Swift standard library (SE-0302) that marks types safe for passing between isolation domains. An isolation domain means an actor, a Task, or a @MainActor context. Sendable contains no requirements — it acts as a safety marker.

Why Is Sendable Needed

Before Swift 5.5, developers could pass any object between threads via DispatchQueue, and the compiler did not check whether it was safe. Sendable closes this gap: now the compiler itself tracks cross-thread transfers and blocks unsafe ones. This makes concurrent code more reliable even before it runs.

swift
struct UserProfile: Sendable {
    let name: String
    let age: Int
}

class NonSendableClass {
    var counter: Int = 0
}

In the example, UserProfile can be safely passed between threads because it is a struct with constant properties of Sendable types. NonSendableClass will cause a compilation error when trying to pass it via await or into a Task.

Which Types Conform to Sendable by Default

Sendable types in Swift fall into three categories: value types with Sendable properties, final classes with immutable state, and functions/closures marked @Sendable. The compiler automatically infers Sendable conformance for value types.

Value Types and Automatic Conformance

Struct, enum and tuple automatically become Sendable if all their properties and associated values are also Sendable. This is a conservative approach: if at least one property does not conform to Sendable, the entire type will not be recognized as safe.

TypeSendableCondition
StructYes (without explicit declaration)All properties are Sendable
EnumYes (without explicit declaration)All associated values are Sendable
Final classYes (with explicit declaration)All let properties are Sendable, no var
Non-final classNoCannot be Sendable due to inheritance

Basic Types

All built-in Swift types — Int, String, Double, Bool, Optional, Array, Dictionary, Set — conform to Sendable. This makes type composition safe by default. The developer only needs to watch custom classes.

Sendable and Classes: @unchecked Sendable

@unchecked Sendable is a mechanism that allows a class to explicitly declare itself as Sendable, bypassing compiler checks. The developer takes responsibility for the thread safety of such a class. This is useful for Objective-C bridges and optimized structures.

When to Use @unchecked Sendable

@unchecked Sendable is used when a class internally guarantees safety through locks or atomic operations, but the compiler cannot verify this statically. For example, a class with os_unfair_lock or pthread_mutex_t — its thread safety is ensured by code, but Swift does not see it.

swift
final class AtomicCounter: @unchecked Sendable {
    private var value: Int = 0
    private let lock = NSLock()
    
    func increment() {
        lock.lock()
        value += 1
        lock.unlock()
    }
}

Using @unchecked Sendable should be done carefully. It is an explicit signal to other developers: “I checked, this is safe.” An implementation error can lead to hard-to-find races. Before using @unchecked, make sure the type really cannot be rewritten as a value type.

Sendable in Combination with Actor

Actor and Sendable are two sides of the same coin. Actor isolates its state, but to exchange data with the outside world, it must return Sendable types. If an actor method returns a non-Sendable type, the compiler gives a warning or error.

Passing Data Through Sendable

When an actor sends data to external code, this data crosses the isolation boundary. Sendable guarantees that the recipient can safely use it outside the actor. The actor itself remains isolated — its internal state is not exposed.

swift
struct AccountSnapshot: Sendable {
    let id: UUID
    let balance: Double
    let lastUpdated: Date
}

actor BankActor {
    private var balance: Double = 0
    
    func snapshot() async -> AccountSnapshot {
        return AccountSnapshot(
            id: UUID(),
            balance: balance,
            lastUpdated: Date()
        )
    }
}

AccountSnapshot is a Sendable struct that contains only let properties of Sendable types. This approach is a best practice for extracting data from an actor. The state snapshot is passed by value, and the actor does not lose control over its state.

Sendable Functions and Closures

@Sendable is an attribute for functions and closures that guarantees the closure does not capture non-Sendable data in a mutable way. When a closure is passed to a Task or actor method, it must be Sendable.

Capture Check in @Sendable Closures

The compiler checks that the @Sendable closure does not capture mutable references to classes. Capturing let properties of Sendable types is allowed. Capturing a var variable of a reference type will cause an error because the closure could be executed simultaneously with mutation.

swift
func performAsync(operation: @Sendable () -> Void) {
    Task {
        await operation()
    }
}

let constant = "Safe"
var counter = 0
// counter — capturing var mutates counter, compile error
performAsync { // ❌ Mutation of captured var
    print(constant)
}

The rule is simple: a @Sendable closure can only capture data that is itself Sendable and will not be mutated from outside. For classes, capturing a weak reference weak self is allowed if the class is not marked as Sendable. This prevents classic retain cycles and race conditions.

Frequently Asked Questions

What is Sendable in Swift in simple words?

Sendable is a marker that tells the compiler: “this type can be safely passed between threads.” Structs and constants are usually Sendable by default, classes are not unless explicitly stated.

Which types automatically conform to Sendable?

Value types (struct, enum) with Sendable properties, final classes with immutable state, and all basic Swift types: Int, String, Double, Bool, Array, Dictionary, Optional — conform to Sendable automatically.

What is @unchecked Sendable?

@unchecked Sendable is a way to declare a class as Sendable without static compiler checks. The developer guarantees safety on their own, often through locks or atomic operations.

Why is Sendable needed for Actor?

Actor isolates its state, but when returning data to external code, this data crosses the isolation boundary. Sendable guarantees that the recipient will not encounter races when using this data outside the actor.

How to make a class Sendable in Swift?

Add the Sendable protocol to a final class, all of whose properties are constants of Sendable types. If the class uses locks, you can use @unchecked Sendable, but this requires caution.

Summary

  • Sendable — a marker protocol for safe cross-thread data transfer
  • Value types automatically conform to Sendable when conditions are met
  • Classes require explicit Sendable or @unchecked Sendable declaration
  • Actor returns data to external code only through Sendable types
  • @Sendable closure attribute checks the correctness of variable capture
  • The compiler statically checks Sendable conformance, preventing races at build time

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also