Thumbtack Abdul Moiz Jul 3, 2024

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

Critical Insight

Thumbtack created a reusable pattern using protocol associated types that lets teams adopt Swift actors while maintaining full unit test coverage.

The article includes complete code examples showing how the injection and stubbing mechanism works under the hood.

About This Article

Problem

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.

Solution

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.

Impact

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.