Swift Actor in Unit Tests
Article Summary
Abdul Moiz from Thumbtack reveals how Swift 6's concurrency checks broke their entire test suite. Their solution? A clever protocol-based pattern that makes actors mockable without sacrificing thread safety.
When Thumbtack's iOS team upgraded to Xcode 15.3, Swift's new concurrency warnings forced them to adopt actors for thread safety. But actors can't be subclassed, breaking their dependency injection and unit testing framework. The team developed a generalized solution using associated types that works across their entire codebase.
Key Takeaways
- Swift actors provide automatic thread safety but can't be mocked in tests
- Protocol with associatedtype enables stubbing different actors with same interface
- Solution works with existing dependency injection without learning curve
- Approach scales to any actor without custom implementation per type
Thumbtack created a reusable pattern using protocol associated types that lets teams adopt Swift actors while maintaining full unit test coverage.
About This Article
After upgrading to Xcode 15.3, Thumbtack's iOS team hit Swift concurrency warnings about passing non-sendable types outside of main actor-isolated context. This could introduce data races. They had to pick between final classes, @unchecked Sendable with manual locking, or actors that couldn't be subclassed for testing.
Abdul Moiz's team built an ActorProtocol with an associatedtype ProtocolType. Both production actors and mock actors could conform to the same protocol interface. They used an Injector singleton to store and retrieve mocks by ObjectIdentifier, so they didn't need custom swizzling for each actor.
The pattern removed the need to reinvent locking mechanisms or work around concurrency checks. Thumbtack migrated to Xcode 15.3 while keeping full unit test coverage and snapshot testing frameworks intact. Existing code integration tools continued to work without breaking.