When Apple launched Xcode 16 final 12 months, they made the Swift 6 compiler obtainable together with it. Which means we are able to create new tasks utilizing Swift 6 and its compile-time knowledge race protections.
Nevertheless, the massive query for a lot of builders is: Is 2025 the precise time to undertake Swift 6 absolutely, or ought to we follow Swift 5 for now?
On this put up, I received’t offer you a definitive reply. As an alternative, I’ll share my perspective and reasoning that can assist you resolve whether or not adopting Swift 6 is best for you and your venture(s).
The suitable reply is determined by a great deal of variables just like the venture you’re employed on, the workforce you’re employed with, and your information of Swift Concurrency.
Xcode 16, present tasks, and Swift 6
If you happen to’ve opened an present venture in Xcode 16, you won’t have seen any instant adjustments. Whereas the Swift 6 compiler is utilized in Xcode 16 for all tasks, Xcode defaults to the Swift 5 language mode for present tasks.
If you happen to’ve skilled earlier main migrations in Swift, you’ll do not forget that Xcode would normally immediate you to make adjustments to your venture with the intention to ensure your venture nonetheless works. This occurred for the migration from Swift 1.2 to Swift 2, and from Swift 2 to Swift 3.
We acquired a brand new compiler, and we had been pressured to undertake the brand new Swift language model that got here together with it.
Since then, the compiler has gained some “language modes”, and the Swift 6 compiler comes with a Swift 5 language mode.
The Swift 5 language mode permits the Swift 6 compiler to operate with out imposing all of the stricter guidelines of Swift 6. For instance, the Swift 5 language mode will make it in order that compile-time knowledge race protections usually are not turned on.
So, once we speak about adopting Swift 6, we’re actually speaking about opting into the Swift 6 language mode.
Present tasks which might be opened in Xcode 16 will, routinely, use the Swift 5 language mode. That’s why your venture nonetheless compiles completely fantastic with out adopting Swift 6.
What about new tasks?
New tasks in Xcode 16 additionally default to Swift 5 language mode. Nevertheless, Swift packages created with the Swift 6 toolchain default to Swift 6 language mode until explicitly configured in any other case. This distinction is vital, as a result of while you create new packages you’re working in a unique language mode than venture (and that’s completely fantastic).
If you happen to’re excited by enabling the Swift 6 language mode for present tasks or packages, I’ve some weblog posts about that right here:
Challenges of Adopting Swift 6
Switching to Swift 6 language mode could make tasks that compiled simply fantastic with Swift 5 break fully. For instance, you’ll run into errors about capturing non-sendable parameters, sendable closures, and actor isolation.
Some fixes are easy—like making an immutable object explicitly sendable or refactoring objects which might be utilized in async features into actors. Nevertheless, different points, particularly these involving crossing isolation boundaries, might be a lot trickier to repair.
For instance, including actors to resolve sendability errors typically requires refactoring synchronous code into asynchronous code, resulting in a ripple impact all through your codebase. Even seemingly easy interactions with an actor require await, even for non-async features as a result of actors function in their very own isolation contexts.
Adopting actors is often a job that may take a lot, for much longer than you may count on initially.
Resolving errors with @MainActor
A typical workaround is to liberally apply @MainActor annotations. Whereas this reduces concurrency-related errors by forcing most code to run on the primary thread, it’s not all the time the answer that you simply’re on the lookout for. Whereas not inherently fallacious, this strategy ought to be used with warning.
Lowering crossing of isolation boundaries
Apple acknowledges the challenges of adopting Swift 6, particularly for present tasks. One important side of Swift Concurrency that may make adoption tough is how non-isolated asynchronous features inherit isolation contexts. At the moment, nonisolated async features run on a background thread until explicitly remoted, which may result in pointless crossing of isolation boundaries.
Apple is exploring methods for such features to inherit the caller’s isolation context, doubtlessly decreasing sendability errors and making adoption of Swift 6 way more easy.
So, ought to we undertake Swift 6?
For present tasks, I like to recommend continuing cautiously. Persist with Swift 5 language mode until:
• Your venture is small and manageable for migration.
• You’ve got a robust understanding of concurrency ideas and might decide to resolving sendability points.
New tasks might be constructed with Swift 6 language mode from the beginning, however be ready for challenges, particularly when interacting with Apple’s frameworks, which can lack full concurrency help.
If you happen to’re modularizing your codebase with Swift packages, I like to recommend utilizing Swift 6 language mode in your (new) packages, as packages typically have fewer dependencies on Apple’s frameworks and are simpler to adapt and you may have Swift 5 and Swift 6 modules in the identical venture.
On the brink of undertake Swift 6
Earlier than adopting Swift 6, make sure you perceive:
• Sendability and easy methods to resolve associated errors.
• Using actors and their affect on isolation and asynchronicity.
• Find out how to navigate ambiguous compiler errors.
I cowl all of those matters and extra in my e-book, Sensible Swift Concurrency in addition to my workshops. You can even evaluation and research Swift evolution proposals and discussion board discussions to get sense of how Swift Concurrency works.
If you happen to’ve began adopting Swift 6 or determined to carry off, I’d love to listen to your experiences! Join with me on X, BlueSky, or Mastodon.