0
0
Swiftprogramming~15 mins

Sendable protocol for thread safety in Swift - Deep Dive

Choose your learning style9 modes available
Overview - Sendable protocol for thread safety
What is it?
The Sendable protocol in Swift is a way to mark types as safe to use across different threads. It tells the compiler that instances of a type can be shared or passed between threads without causing data races or unexpected behavior. This helps developers write safer concurrent code by catching potential thread-safety issues at compile time.
Why it matters
Without the Sendable protocol, sharing data between threads can lead to bugs that are hard to find, like crashes or corrupted data. The protocol helps prevent these problems by enforcing rules about what can be safely shared. This makes apps more reliable and easier to maintain, especially as they grow more complex and use more concurrency.
Where it fits
Before learning about Sendable, you should understand basic Swift types, concurrency concepts like threads and tasks, and value vs reference types. After mastering Sendable, you can explore Swift's concurrency features like actors, async/await, and structured concurrency to build safe and efficient multi-threaded programs.
Mental Model
Core Idea
Sendable means a type can be safely sent or shared between threads without causing data conflicts.
Think of it like...
Imagine sending a sealed, unbreakable box through a busy mail system; no matter how many hands touch it, the contents stay safe and unchanged.
┌───────────────┐       ┌───────────────┐
│ Thread A      │──────▶│ Thread B      │
│ (Sender)      │       │ (Receiver)    │
│               │       │               │
│ ┌───────────┐ │       │               │
│ │ Sendable  │ │       │               │
│ │ Data      │ │       │               │
│ └───────────┘ │       │               │
└───────────────┘       └───────────────┘

Sendable ensures data inside the box is safe to pass between threads.
Build-Up - 7 Steps
1
FoundationUnderstanding Thread Safety Basics
🤔
Concept: Introduce what thread safety means and why it matters in programming.
Thread safety means making sure that when multiple threads access the same data, they do not cause errors or unexpected results. Without thread safety, two threads might try to change the same data at the same time, causing bugs.
Result
You understand that sharing data between threads can cause problems if not handled carefully.
Knowing why thread safety matters helps you appreciate why special rules or tools are needed to avoid hard-to-find bugs.
2
FoundationSwift Types and Concurrency Basics
🤔
Concept: Learn about Swift's value and reference types and basic concurrency concepts.
Swift has value types (like structs) that copy data when assigned, and reference types (like classes) that share data by reference. Concurrency means running code at the same time on different threads or tasks. Understanding these helps see why some types are safer to share than others.
Result
You can distinguish between types that are naturally safe to share and those that need care.
Recognizing how data is shared or copied is key to understanding thread safety in Swift.
3
IntermediateWhat is the Sendable Protocol?
🤔Before reading on: do you think Sendable applies only to classes, only to structs, or both? Commit to your answer.
Concept: Introduce the Sendable protocol as a marker for types safe to share across threads.
Sendable is a protocol in Swift that types can conform to if they guarantee safe sharing between threads. Both structs and classes can conform, but classes must ensure thread safety internally. The compiler uses Sendable to check your code for safety.
Result
You know that Sendable marks types as safe for concurrency and that the compiler helps enforce this.
Understanding Sendable as a contract helps you write safer concurrent code and catch mistakes early.
4
IntermediateAutomatic and Manual Sendable Conformance
🤔Before reading on: do you think Swift automatically makes all structs Sendable, or only some? Commit to your answer.
Concept: Explain when Swift automatically marks types as Sendable and when you must declare it manually.
Swift automatically makes many simple value types Sendable if all their stored properties are Sendable. For complex types or classes, you must declare conformance and ensure thread safety yourself. You can also use @unchecked Sendable to bypass checks, but this is risky.
Result
You understand when Sendable is automatic and when manual work is needed.
Knowing the rules for automatic conformance helps avoid unnecessary code and spot when you must be careful.
5
IntermediateUsing Sendable with Async and Actors
🤔Before reading on: do you think Sendable is required only for data passed between threads, or also for data inside actors? Commit to your answer.
Concept: Show how Sendable works with Swift's async/await and actor model for concurrency.
Actors isolate data to one thread, but when you pass data to or from actors or async tasks, that data must be Sendable. This ensures safe communication between concurrent parts of your program.
Result
You see how Sendable fits into Swift's concurrency features to keep data safe.
Understanding Sendable's role in async and actors clarifies how Swift prevents data races in modern concurrency.
6
AdvancedHandling Reference Types and @unchecked Sendable
🤔Before reading on: do you think marking a class as Sendable guarantees thread safety automatically? Commit to your answer.
Concept: Discuss the challenges of making classes Sendable and the risks of using @unchecked Sendable.
Classes share references, so marking them Sendable means you promise to handle thread safety yourself, often with locks or immutable state. Using @unchecked Sendable skips compiler checks but can cause runtime bugs if you're not careful.
Result
You understand the responsibility and risks when making reference types Sendable.
Knowing the limits of Sendable with classes helps prevent subtle concurrency bugs in production.
7
ExpertCompiler Enforcement and Runtime Guarantees
🤔Before reading on: do you think Sendable guarantees thread safety at runtime, or only helps the compiler check your code? Commit to your answer.
Concept: Explain how the compiler uses Sendable to enforce safety and what it cannot guarantee at runtime.
The compiler uses Sendable to check that only safe types cross thread boundaries, preventing many bugs before running. However, it cannot enforce thread safety inside classes at runtime, so developers must still design carefully. This balance helps catch many errors early but requires discipline.
Result
You appreciate the limits and power of Sendable in Swift's concurrency safety.
Understanding compiler vs runtime roles clarifies how Sendable fits into the bigger picture of safe concurrent programming.
Under the Hood
Sendable is a marker protocol that the Swift compiler uses during type checking to verify that values passed between concurrent contexts are safe to share. For value types, the compiler checks that all stored properties are also Sendable, ensuring immutability or safe copying. For reference types, the compiler requires explicit conformance and trusts the developer to ensure thread safety, as runtime enforcement is not possible. This static checking helps prevent data races by restricting what can be sent across threads.
Why designed this way?
Swift's concurrency model aims to catch as many errors as possible at compile time to avoid unpredictable runtime bugs. The Sendable protocol was introduced to provide a clear, compiler-enforced contract for thread safety without heavy runtime overhead. Alternatives like runtime locks or dynamic checks were rejected because they add complexity and performance costs. This design balances safety, performance, and developer control.
┌─────────────────────────────┐
│ Swift Compiler Checks Sendable │
├───────────────┬─────────────┤
│ Value Types   │ Reference Types │
│ (Structs, Enums)│ (Classes)      │
├───────────────┼─────────────┤
│ Checks all    │ Requires     │
│ properties   │ explicit     │
│ are Sendable  │ conformance  │
│ (automatic)  │ and manual   │
│              │ thread safety│
└───────────────┴─────────────┘

Runtime trusts developer for classes; compiler enforces for values.
Myth Busters - 4 Common Misconceptions
Quick: Does marking a class as Sendable automatically make it thread-safe? Commit to yes or no.
Common Belief:If a class conforms to Sendable, it is automatically thread-safe.
Tap to reveal reality
Reality:Marking a class as Sendable only promises the developer has ensured thread safety; the compiler does not enforce it for classes.
Why it matters:Assuming automatic safety can lead to data races and crashes if the class is not properly synchronized.
Quick: Are all Swift structs automatically Sendable? Commit to yes or no.
Common Belief:All Swift structs are Sendable by default.
Tap to reveal reality
Reality:Only structs whose stored properties are all Sendable get automatic Sendable conformance.
Why it matters:Using non-Sendable structs in concurrency can cause compiler errors or unsafe behavior.
Quick: Does Sendable guarantee thread safety at runtime? Commit to yes or no.
Common Belief:Sendable guarantees thread safety both at compile time and runtime.
Tap to reveal reality
Reality:Sendable only helps the compiler check safety; runtime thread safety depends on the developer's code.
Why it matters:Relying solely on Sendable without proper synchronization can cause subtle bugs.
Quick: Can you safely use @unchecked Sendable without any risk? Commit to yes or no.
Common Belief:Using @unchecked Sendable is safe because it tells the compiler to trust you.
Tap to reveal reality
Reality:@unchecked Sendable disables compiler checks and can lead to unsafe concurrency if misused.
Why it matters:Misusing @unchecked Sendable can introduce hard-to-debug data races and crashes.
Expert Zone
1
Sendable conformance for classes requires careful design, often involving immutable state or synchronization primitives like locks or queues.
2
The compiler's automatic Sendable synthesis only works if all stored properties conform, which can be tricky with generic or complex types.
3
Using @unchecked Sendable is a powerful but dangerous escape hatch that should be reserved for cases where the developer can guarantee safety by other means.
When NOT to use
Avoid using Sendable for types that manage mutable shared state without proper synchronization. Instead, use actors or explicit locking mechanisms to ensure thread safety. Also, do not rely on @unchecked Sendable unless you have full control and understanding of concurrency risks.
Production Patterns
In production Swift code, Sendable is commonly used with value types passed between async tasks and actors. Classes that conform to Sendable often encapsulate immutable data or use internal synchronization. Developers combine Sendable with actors to build safe concurrent systems, leveraging compiler checks to minimize bugs.
Connections
Immutable Data Structures
Sendable builds on the idea of immutability to ensure safe sharing.
Understanding immutable data helps grasp why many Sendable types are value types that do not change after creation.
Actor Model (Computer Science)
Sendable complements actors by defining safe data exchange between isolated actors.
Knowing actor isolation clarifies why Sendable is needed to safely pass data across actor boundaries.
Parcel Delivery Systems
Sendable is like a guarantee that a parcel can be safely transported without damage.
This cross-domain view highlights the importance of clear contracts to ensure safety in complex systems.
Common Pitfalls
#1Marking a mutable class as Sendable without synchronization.
Wrong approach:class Counter: Sendable { var count = 0 func increment() { count += 1 } }
Correct approach:actor Counter { var count = 0 func increment() { count += 1 } }
Root cause:Misunderstanding that Sendable does not automatically make mutable classes thread-safe.
#2Assuming all structs are Sendable regardless of their properties.
Wrong approach:struct Wrapper: Sendable { var data: UnsafeMutablePointer }
Correct approach:struct Wrapper { var data: Int } // Only use Sendable if all properties conform
Root cause:Not realizing that non-Sendable stored properties prevent automatic Sendable conformance.
#3Using @unchecked Sendable without ensuring thread safety.
Wrong approach:class SharedResource: @unchecked Sendable { var value = 0 }
Correct approach:actor SharedResource { var value = 0 }
Root cause:Ignoring the risks of disabling compiler checks with @unchecked Sendable.
Key Takeaways
Sendable is a protocol that marks types as safe to share across threads in Swift's concurrency model.
The compiler uses Sendable to check thread safety at compile time, especially for value types with Sendable properties.
Classes can conform to Sendable but require manual thread safety measures; the compiler does not enforce this for reference types.
Using Sendable correctly helps prevent data races and makes concurrent Swift code safer and more reliable.
Understanding the limits of Sendable and when to use actors or locks is essential for writing robust concurrent programs.