Article
Sendable in Practice
You've read thisBitesize
Sendable means a value is safe to move between concurrency domains.
struct UserSnapshot: Sendable {
let id: UUID
let name: String
}
Value types with immutable stored properties are usually the easiest fit.
More detail
You see Sendable most often around tasks, actors, and async APIs that can cross concurrency boundaries.
Trouble usually starts with reference types:
- mutable classes
- closures that capture mutable state
- older code without concurrency annotations
When the compiler flags a Sendable issue, it is usually pointing at a real ownership or mutation concern rather than being overly strict.
This is why Sendable often feels like design pressure rather than a random type-system rule. It forces you to ask whether a value is actually stable enough to move around safely once multiple concurrent contexts are involved.
For structs, the answer is often easy. For classes, the answer depends on whether the class has mutable shared state, whether that state is synchronized, and whether the type is meant to cross concurrency boundaries at all.
Those questions are useful even when the compiler is annoying. They expose assumptions about mutation and sharing that might otherwise remain hidden until much later.
Deep dive
The practical question behind Sendable is: can this value be shared or transferred without creating unsafe concurrent access?
That is why immutable structs fit so naturally. A class with mutable state has to justify how it remains safe. Sometimes that means actor isolation. Sometimes it means synchronization. Sometimes it means the type should simply not cross the boundary.
The strongest habit here is to treat Sendable diagnostics as design feedback. They often reveal unclear ownership, hidden mutation, or APIs that are trying to do too much.
There is also an architectural implication. Types that are cleanly Sendable tend to be easier to move through modern Swift concurrency features because they carry fewer hidden assumptions. That makes them more reusable and less fragile.
By contrast, types that struggle with Sendable are often holding too much mutable responsibility. They may need refactoring, stronger encapsulation, or a more explicit concurrency boundary around them.
A useful pattern is to keep the data you move around as simple immutable snapshots, and keep the mutable coordination logic in actors or other well-bounded components. That separation makes the compiler happier, but more importantly it usually makes the design healthier.
The deeper lesson is that concurrency safety is not only about annotations. Sendable is valuable because it pushes the codebase toward clearer transfer semantics and less accidental shared state.
When you lean into that instead of fighting it, the warnings become surprisingly informative. They stop being friction and start acting like a guide toward more predictable concurrent code.
Finished the deep dive?
You made it to the end.
Mark this article as read once you have worked through the full piece. It is a small way to keep track of what you have genuinely finished.
More in this area
Keep the thread going.
Jump sideways into the related ideas that sit closest to this piece and keep the same mental context alive.