December 05, 2025
There was a time when compiler settings were something I totally ignored. I didn’t follow along with upcoming features at all. I just used the defaults and didn’t think about it. But that’s a lot harder to get away with right now. We’re in a protracted period where not only are settings very important, they are also deeply and persistently misunderstood.
The settings you use can have a profound effect on your code.
But there are a lot of them! And I think it’s pretty reasonable that you might not want to dig into every single one. So, here’s an attempt at some guidance.
TL;DR:
There are 21 settings, but only 5 are of any real concern.
You can just ignore these for now: ExistentialAny, InternalImportsByDefault, MemberImportVisibility.
These are …
December 05, 2025
There was a time when compiler settings were something I totally ignored. I didn’t follow along with upcoming features at all. I just used the defaults and didn’t think about it. But that’s a lot harder to get away with right now. We’re in a protracted period where not only are settings very important, they are also deeply and persistently misunderstood.
The settings you use can have a profound effect on your code.
But there are a lot of them! And I think it’s pretty reasonable that you might not want to dig into every single one. So, here’s an attempt at some guidance.
TL;DR:
There are 21 settings, but only 5 are of any real concern.
You can just ignore these for now: ExistentialAny, InternalImportsByDefault, MemberImportVisibility.
These are definitely worth consideration, but may require understanding: InferIsolatedConformances, NonisolatedNonsendingByDefault.
These are the big ones from the 6 language mode and have serious implications: DynamicActorIsolation, GlobalConcurrency StrictConcurrency.
Technically a mode, and one many people have trouble with: DefaultActorIsolation of MainActor.
You can, and probably should, just turn everything else on.
More details? Ok let’s go.
Compiler version
On a surprisingly regular basis, someone will tell me they using Swift 5.10. And I get to ask, as politely as I am able, "are you sure about that?". Because the Swift compiler is bundled with Xcode. To have Swift 5.10, you’d need to be using Xcode 15, which you are almost certainly not doing.
The source of this confusion is very understandable though. The Swift compiler, for a very long time now, has a concept of "language mode". Swift 6.2, the version included with Xcode 26, supports four language modes: 4, 4.2, 5, and 6. That’s right, it still supports a language mode from 2017. But a language mode is, typically, just an alias for a big group of settings. Instead of having to flip on all the settings that went into the 4.2 and 5.0 releases individually, you can just set "5 mode" and be done.
(There actually are people that, for various reasons, do in fact use old build environments. But, I’m not sure how relevant this is to general discussion since it represents a pretty special case.)
Language Mode
I’m just going to guess that you want either the 5 or 6 language modes. For Xcode targets, you must use the Xcode setting SWIFT_VERSION. Now that the term "Language Mode" has been formalized, I think it’s about time that Xcode adopt this terminology. Please.
Controlling language mode for your Swift packages is completely independent of your Xcode settings. There are a variety of ways to do this in SPM. But my personal favorite is to use the simplicity of the defaults provided by swift-tools-version when I can, and an explicit .swiftLanguageMode otherwise.
Aside: Versioned Package.swift files
A phenomenon I have starting running into more and more are versioned Package.swift files. It looks like this:
Package.swift
Package@6.2.0.swift
I believe this purpose of this is to give you a way to a) use SPM Package description APIs that aren’t available with b) earlier Swift toolchains that you need to support. However, that’s an unusual thing to need! So I often peek inside to see what’s going on. And frequently, I discover that no new package description APIs are actually being used. This is bad because, aside from the extra complexity, it has the potential to introduce build configurations that you "support" but never actually test.
If you are using versioned package files, it’s worth checking in to make sure it is doing what you want.
(A very powerful new feature in Swift 6.1, package traits, is now becoming more common in the server community in particular. This is a very valid reason to use versioned Package.swift files. But today, traits are not supported by Xcode.)
Settings
At the time of writing, the current public release of Swift supports a total of 21 upcoming features. This means these things will all, eventually, become "the way". Of these 16 are part of the 6 language mode. We can assume that one day, some even newer language mode will be added, say 7, which will enable the remaining 5.
Language modes are pretty much just groups of settings. But, there is exactly one difference (I know of) between the 5 and 6 modes that isn’t a setting. In 6 mode, concurrency warnings become errors. Aside from that, it’s possible to be get all of the changes of 6 while still technically being in 5 mode.
Xcode 16 gained the ability to control Swift compiler features via build settings, which is a lot nicer than having to pass around compiler flags. Again, the story for SPM is more complex. I often use this technique from Keith Harrison. It’s particularly useful for packages with a lot of targets.
It is, however, very important to understand that your Xcode and SPM build settings are independent. Changes to one will not impact the other. And, further, changes to your projects/modules will not impact your dependencies. Those are under the control of their authors.
Far Off Things You can Ignore
Now, finally, we can actually have a look at some of these settings. Let’s start with the features that are the furthest out.
You can read about these if you want. They solve real problems and could be helpful for you. Or maybe you’d like to get a jump on future-proofing your code. But, there’s no pressure here to do anything.
I would classify all three of these as totally safe to ignore. Easy.
Far Off Things You Should Think About
InferIsolatedConformances
Basically, MainActor’ing stuff might cause problems with protocol conformances and this feature can make those problems go away. It’s cool.
However, I still have limited experience with this one. I’d say I’m only about 75% sure that this is a good idea for people that don’t understand it. The feature does have the potential to result in some hard-to-understand-and-fix issues. But, I’m not sure yet if that’s actually a real problem or just theoretical.
NonisolatedNonsendingByDefault
This changes the semantics of nonisolated async functions. This is my favorite feature of Swift 6.2, and often the only setting of consequence when people talk about "Approachable Concurrency".
Here’s a quick test:
class SomeClass {
func asyncMethod() async {
}
}
If you know when/why a type like this can be a problem, then you can adopt this setting at your leisure. However, if you are looking at this code and thinking "ummm this is just a normal class?" then I think you almost certainly need to enable this feature so the language works like you think it does.
You can read more about this. And that’s important to do, because opting into this one may require a migration and that documentation explains how to do it.
Also, don’t forget that "Approachable Concurrency" is an Xcode thing, so you’ll still have to use this setting directly within SPM.
6 Mode Stuff You Should Just Turn On
These are all settings that are enabled by 6 language mode. They are also, in my opinion, very unlikely to introduce any source incompatibilities in your project. If you turn them on and everything continues to build, you should be just fine. Some of these will also be very useful when using concurrency, but should not matter if you aren’t interested in that stuff.
BareSlashRegexLiteralsConciseMagicFileDeprecateApplicationMainForwardTrailingClosuresImplicitOpenExistentialsImportObjcForwardDeclarationsNonfrozenEnumExhaustivityDisableOutwardActorInferenceGlobalActorIsolatedTypesUsabilityInferSendableFromCapturesRegionBasedIsolationIsolatedDefaultValues
There’s documentation about all the settings that apply to 6 mode specifically if you want to learn about any of these. But for these I don’t even think that is necessary.
6 Mode Stuff You Cannot Just Turn On
These last three are also enabled by the 6 language mode. None of these are easy decisions and you should not enable them without understanding what they do.
GlobalConcurrency
Applies concurrency-safety rules to global values. This will almost certainly introduce some concurrency warnings. But nothing like what can happen with complete checking.
This is not a bad way to ease into a Swift 6 migration. Often the problems this finds are easy to fix. But, this setting only covers a small fraction of what StrictConcurrency does. Also, because you aren’t seeing the full picture, it can sometimes be hard to understand the implications of any changes you make.
DynamicActorIsolation
This is a hugely-consequential settings. It will transform potential data races into runtime crashes. You must understand the implications of this before enabling it directly, or indirectly via 6 mode. I cover the general idea as it applies to Combine, but all APIs can be subject to this problem.
StrictConcurrency
This is the big dial for how much concurrency checking you want to see. 6 mode sets this to "complete", but I think "targeted" has a lot of value as well.
Leaving this setting at "minimal" while also using the async or await keywords is risky. It is very hard to use a compiler-based concurrency system correctly when the compiler’s checks are disabled. But as I wrote above, turning on NonisolatedNonsendingByDefault can transform some correctness issues into performance problems. I think that’s probably the right trade-off if you are using minimal.
It is worth noting that setting this to "complete" will implicitly enable IsolatedDefaultValues, GlobalConcurrency, and RegionBasedIsolation.
DefaultActorIsolation
This is not an upcoming feature. It is not a part of Xcode’s "Approchable Concurrency" setting. It is a permanent mode that will always be optional. In Xcode you use SWIFT_DEFAULT_ACTOR_ISOLATION and in SPM you do it with the special .defaultIsolation setting.
I think you should tread very carefully here. You should also be aware that setting the default isolation to MainActor has the side-effect of implicitly enabling InferIsolatedConformances.
"Recommendations"
Turn on the easy stuff. Ignore the far off stuff. You have to understand the implications of the others before you flip the switch. And you should feel good about taking your time. If dealing with all this isn’t something you want to do, you can definitely wait.
Did you know that I do consulting for concurrency and Swift 6 migrations? If you think I could help, get in touch.