How do you work with Non-sendable types in swift?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Understanding Non-Sendable Types in Swift
When working with concurrency in Swift, understanding "sendable" types is crucial. Apple's Swift Concurrency model introduces the Sendable protocol to ensure data integrity across concurrent executions. While some types are inherently sendable, others are "non-sendable" or need special attention to make them conform to Sendable. This article delves into handling these non-sendable types, focusing on technical explanations and examples.
What are Sendable and Non-Sendable Types?
At its core, the Sendable protocol guarantees safe data passage across Swift's concurrency boundaries, such as among threads or asynchronous task hierarchies. A type conforming to Sendable ensures that using it concurrently doesn’t lead to data races or undefined behavior.
Sendable Protocol
- Sendable Types: Types that can safely be sent across concurrency domains without risking undefined behavior. For example, all value types like integers, floats, and strings are inherently
Sendable. - Non-Sendable Types: Types that are not conforming to
Sendable, typically due to references to shared mutable data, such as classes or external resources like file handles and network connections.
Identifying Non-Sendable Types
Swift 5.5 introduced compile-time checks to automatically assert the Sendable conformance of types used across actors or tasks. The compiler will produce errors if non-sendable types are used unsafely across concurrency boundaries.
In the example above, attempting to use an instance of MyClass within a Task causes a compile-time error because MyClass does not conform to Sendable.
Working with Non-Sendable Types
Custom Sendable Conformance
For types you control, consider conforming to Sendable explicitly, ensuring the structure doesn’t mutate or use isolation techniques.
Key Strategies:
- Use of Isolation: Employ queues or other synchronization mechanisms to prevent data races.
- Final and
@uncheckedSendable: Mark classes asfinalto avoid subclassing issues, and use@unchecked Sendablejudiciously to bypass the compiler’s checks, indicating you take full responsibility for ensuring safety.
Sendable Design Alternatives
Sometimes the right design option might be to avoid needing the type to be sendable:
- Immutability: Convert mutable types into immutable variants using
structsorenums. - Data Isolation: Use concurrent-safe containers or wrapper classes to encapsulate mutable data safely.
Common Pitfalls and Considerations
There are some potential pitfalls and considerations when working with non-sendable types:
| Issue | Description | Solution |
| Shared State | Mutable shared state can lead to race conditions. | Use locks or queues for synchronization. |
| Complexity Overhead | Conforming complex types with dependencies to Sendable can result in intricate code. | Simplify design or refactor into multiple sendable parts. |
| Performance | Over-synchronization can impact performance. | Evaluate the necessity of synchronization on a case-by-case basis. |
Conclusion
Swift's concurrency model facilitates safe multi-threaded operations, with Sendable types playing a pivotal role in ensuring data safety across task boundaries. Handling non-sendable types requires careful consideration of immutability, isolation, and synchronization strategies. Employ these practices effectively to leverage Swift's concurrency model, maintaining both safety and performance in your applications.
By understanding and applying these techniques, you can efficiently design applications that uphold concurrency safety, making the most of modern Swift's robust capabilities.

