With the release of Swift 6, the differences between the new language version and Swift Concurrency became a bit unclear. Some say Swift 6 is all about Swift Concurrency, while others think it has nothing to do with each other.
Swift 6 and Swift Concurrency are closely related, but they are not the same thing. While Swift Concurrency was introduced in Swift 5.5, it plays a crucial role in Swift 6’s long-term goals.
Swift 6: A major evolution of the language
Swift 6 is focused on safety and predictability, especially in how Swift handles concurrency. The main goal is to make Swift safer by detecting data races at compile time.
To achieve this, Swift 6 introduces strict concurrency checking, which enforces rules about how data is accessed across threads. This emphasizes the relationship between Swift 6 and Swift Concurrency.
Swift Concurrency: The Foundation for Swift 6
The release of structured concurrency and its features like async/await, tasks, and actors have been the foundation of Swift 6. Safety and predictability are only possible with the many improvements Swift Concurrency brings.
Strict concurrency checking and Swift Concurrency are important here: Swift 6 language mode enables compile-time checks to ensure your code is asynchronously safe. The compiler cannot yet catch all cases since Swift 6 is still being improved, but it will be more confident your code is safer after enabling strict concurrency. For those wondering, we’ll cover the migration to Swift 6 and use strict concurrency checking in more detail later in this course.
Swift Concurrency will also be more enforced by Xcode after enabling Swift 6 language mode. Your project will be checked for safety, so be prepared for several migrations when you decide to start this journey. Once again, this course will help you with this.
Key changes in Swift 6
This course is focused on concurrency and Swift 6, so we won’t go into much detail on other features that got released in Swift 6. Yet, if you are curious and you want to learn more, check out these detailed articles on new features:
- Typed throws in Swift explained with code examples
- SE-409 Access level on imports
- Or explore this extensive list of merged proposals
I do want to highlight the improvements made related to concurrency.
Eliminating all data races
Swift 6’s goal has always been to eliminate all data races. Once you migrate your projects to Swift 6, you’ll notice several warnings related to the Sendable protocol and concurrency. These warnings guide you towards making your project thread-safe, eliminating data races and race conditions (they’re not the same, more on that later).
Some of your app’s crashes are likely related to data races, while you have no clue how to reproduce them. Don’t be surprised to see them disappear after migrating successfully.
Strict Concurrency Checking
To get to a point of eliminating all data races, you need to have strict concurrency checking in place. That’s why you’ll see a lot of warnings and errors in your projects as soon as you switch to Swift 6 language mode (how to do that follows later in this course).
Stricter rules make concurrent code safer and help you to apply the concept of Swift concurrency correctly.
More deterministic execution
Enforcing the previous two goals will result in a more deterministic execution. Your code should become more predictable, reducing unexpected runtime issues. You might have been battling those EXC_BAD_ACCESS crashes in the past—hopefully, this is something you won’t have to do anymore after migrating to Swift 6.
Code That Worked in Swift 5.x May Need Updates
Some concurrency-related patterns that compiled successfully in Swift 5.x might need adjustments to comply with Swift 6’s stricter rules. Most of the time, the compiler will give you a detailed explanation of what’s wrong. If not, you’ll have this course and future lessons as your guidance.
Adopting Swift 6 can be frustrating
At the moment of writing, we just received Swift 6.1 as an update. It contains several improvements like:
- SE-442: Allow TaskGroup’s ChildTaskResult Type To Be Inferred
- SE-449: Allow nonisolated to prevent global actor inference
Like with all Swift releases, you can find an overview of all changes on the official Swift blog here. It’s a great way to stay up to date on what’s new.
What’s interesting to me is that the Swift Language Steering Group recognized the frustration Swift 6 currently brings. In an official vision document they mention:
The Swift 6 language mode provides a baseline of correctness that meets the first goal, but sometimes it comes at the cost of the second, and it can be frustrating to adopt. Now that we have a lot more user experience under our belt as a community, it’s reasonable to ask what we can do in the language to address that problem. This document lays out several potential paths for improving the usability of Swift 6, focusing on two primary use cases:
- Simple situations where programmers aren’t intending to use concurrency at all.
- Adapting an existing code base that uses concurrency libraries which predate Swift’s native concurrency model.
With this document, they set the direction of the future of Swift 6. They also mention three phases on the progressive disclosure path for concurrency:
- Phase 1: Write simple, single-threaded code.
By default, your code runs sequentially—no parallelism, no data races, just straightforward execution. - Phase 2: Write async code without data-race safety errors.
Using async/await lets you suspend execution without introducing parallelism—no shared state issues, just clean async workflows. - Phase 3: Boost performance with parallelism.
Offload work from the main actor, use structured concurrency, and let Swift’s compiler keep your code safe from data races.
Or, in other words:
- Phase 1: No concurrency at all
- Phase 2: Suspend execution without parallism
- Phase 3: Advanced concurrency
They are focusing on improving the language in such way that it becomes easier to slowly adopt Swift Concurrency. There are changes to be expected that will reduce the number of compiler warnings and errors you’ll get and they even plan on automatic migration using a so-called migration build to adopt to these changes more easily.
So, should I wait with migrating to Swift 6?
It’s currently March 17th, and depending on how large your project is you could say: yes, wait since it will likely become easier. However, it’s important to invest in your knowledge and develop the skills to work with concurrency so that by the time you will start migrating, you’re mentally prepared and skilled enough.
You’re in the right place—this course will set you up for success. I’m convinced that by the time all modules are released, we’ll have a better version of Swift 6 in place.
Summary
While Swift 6 and Concurrency are closely related, they have two different origin stories. Swift Concurrency has been the foundation of Swift 6, and the latter enforces rules with strict concurrency checking to create safer and more predictable code. Expect stricter concurrency enforcement in Swift 6, aiming to catch data races at compile time rather than at runtime.
In short, Swift Concurrency paved the way for Swift 6, and Swift 6 is making it the standard way to handle concurrency safely.
That was it for the first module! It’s time for your first quiz to validate whether you’ve caught all the learnings from Module 1.