Preparing My App for Swift 6
How to enable Swift 6 mode for your Xcode projects and for your SwiftPM modules today. And what the migration experience is like.
What is "Swift 6 mode"?
Swift 6 won't be released in 2023 anymore, that has been made clear by Doug Gregor from the Swift Language Workgroup. But did you know that Apple has already shipped parts of Swift 6 in 5.8? Yes, it's true. Some parts of Swift that shipped with Xcode 14.3 a few weeks ago are turned off by default. They will be turned on once Swift 6 comes out, which might take another year, or even longer.
These features introduce some breaking changes to Swift, for example by renaming known APIs, adjusting their behavior, or adding new safety checks to the compiler. But they will all get turned on at some point to help improve our code bases. And we will have to update our projects to play nicely with those changes.
I figured it's a good idea to turn on all features in my projects to see if there's any breaking change for my codebase that I should know of. And of course, I could also make use of some new features, such as BareSlashRegexLiterals
, which makes the /.../
regex literal syntax available for concise Regex initialization.
Thankfully, with Swift 5.8 there's now a unified way of enabling these options: We just need to pass -enable-upcoming-feature
to Swift by providing it in the OTHER_SWIFT_FLAGS
in our projects build settings in Xcode. But we need to know also what features are available, and I couldn't find any place with a good overview (yet). The proposal that introduced this unified option does contain such a list, but it doesn't get updated with newer options added later on, such as the one from SE-0384. So, where can we currently reliably get a list of all supported options?
Swift is open source, so the most reliable place seems the Swift GitHub repository! It includes a Features.def
file which contains entries (link to release/5.8
branch) named UPCOMING_FEATURE
including both the related Swift Evolution proposal number and the Swift version they will be turned on:
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)
UPCOMING_FEATURE(BareSlashRegexLiterals, 354, 6)
UPCOMING_FEATURE(ExistentialAny, 335, 6)
Here are the options with a short description of what they do:
ConciseMagicFile
:
Changes#file
to mean#fileID
rather than#filePath
.ForwardTrailingClosures
:
Removes the backward-scan matching rule.ExistentialAny
:
Requiresany
for existential types.BareSlashRegexLiterals
:
Makes the/.../
regex literal syntax available.
For some reason, two options seem not to be listed there (I'm investigating):
StrictConcurrency
:
Performing complete concurrency checking.ImplicitOpenExistentials
:
Performs implicit opening in additional cases.
More options will ship with later versions, e.g. ImportObjcForwardDeclarations
.
To additionally check my code for proper concurrency support, I chose to also pass -warn-concurrency
(this should actually be the same as StrictConcurrency
if that actually works) and -enable-actor-data-race-checks
.
Migrating my project
If you also want to enable all 5.8
options in your project, like me, copy (⌘C) the following text block, then head to your Xcode projects "Build Settings" tab, search for "Other Swift Flags", select that option in the editor and paste (⌘V) it in:
//:configuration = Debug
OTHER_SWIFT_FLAGS = -enable-upcoming-feature BareSlashRegexLiterals -enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature ExistentialAny -enable-upcoming-feature ForwardTrailingClosures -enable-upcoming-feature ImplicitOpenExistentials -enable-upcoming-feature StrictConcurrency -warn-concurrency -enable-actor-data-race-checks
//:configuration = Release
OTHER_SWIFT_FLAGS = -enable-upcoming-feature BareSlashRegexLiterals -enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature ExistentialAny -enable-upcoming-feature ForwardTrailingClosures -enable-upcoming-feature ImplicitOpenExistentials -enable-upcoming-feature StrictConcurrency -warn-concurrency -enable-actor-data-race-checks
//:completeSettings = some
OTHER_SWIFT_FLAGS
If you are using a SwiftPM-modularized app like me, or if you're working on a Swift package, you will need to additionally pass an array of .enableUpcomingFeature
's to each target via swiftSettings
:
let swiftSettings: [SwiftSetting] = [
.enableUpcomingFeature("BareSlashRegexLiterals"),
.enableUpcomingFeature("ConciseMagicFile"),
.enableUpcomingFeature("ExistentialAny"),
.enableUpcomingFeature("ForwardTrailingClosures"),
.enableUpcomingFeature("ImplicitOpenExistentials"),
.enableUpcomingFeature("StrictConcurrency"),
.unsafeFlags(["-warn-concurrency", "-enable-actor-data-race-checks"]),
]
let package = Package(
// ...
targets: [
// ...
.target(
name: "MyTarget",
dependencies: [/* ... */],
swiftSettings: swiftSettings
),
// ...
]
Don't forget to upgrade your tools version at the top of the file to 5.8
:
That's because Swift doesn't pass along the options you specified for your project target automatically to any modules you import into your project. And that's good news, because that way Swift packages you might be including into your project don't need any adjustments and you can still use these features for your own app code. And vice versa: Package authors can turn these features on for their projects without affecting the code in projects they get consumed in.
After turning these on and building, I ran into four kinds of issues:
1. I had to add the any
keyword in several places – Xcode helped with a Fix-It:
2. For some reason I received a lot of errors for views with a .sheet
modifier. The error pairs stated: "Generic parameter 'Content' could not be inferred" and "Missing argument for parameter 'content' in call". These messages weren't very helpful though, so I first tried to replace the .sheet
content with just a Text
view, but it didn't help. So I've tried turning off the options one by one and the errors disappeared when I turned off ForwardTrailingClosures
, so I kept it turned off. I'm hoping that a better error message will be produced by future versions of Swift to fix them later. There's no hurry, after all. I can retry on Swift 5.9. I didn't have time to investigate now.
3. I had to mark some of my functions returning a TCA WithViewStore
with the @MainActor
attribute, but again, Xcode helped with Fix-Its.
4. In many places, warnings were shown stating: "Non-sendable type '...' passed in call to main actor-isolated function cannot cross actor boundary". So I made these types conform to the Sendable
protocol (learn about Sendable
here).
All else seemed to build fine for my app RemafoX with ~35k lines of Swift code. The whole process took less than 3 hours of my time.
My project is now ready for the future of Swift 🎉 and I can use the new Regex literal feature (let regex = /.*@.*/
). Also, I can't introduce new code that I have to migrate to Swift 6 later, cause I will receive errors right away. 💯
How about your project? Which upcoming features do you want to migrate to?
A drag & drop translator for String Catalog files – it's really easy.
Get it now to machine-translate your app to up to 150 languages!