Enable upcoming Swift features
Helm Pro yearly subscribers now get a 30% discount on RocketSim thanks to contingent pricing on the App Store.
The SE-0362 Swift Evolution proposal, implemented and available from Swift 5.8, allows you to adopt upcoming Swift features in your code on a per-case basis.
This is useful if you want to benefit from features that you would otherwise not be able to in Swift Packages and Xcode targets until Swift 6 is released. The reason for such features not being available by default is to preserve source compatibility with older Swift versions and will not be enabled by default until the next major version of Swift.
There is also an amazing post on swift.org by James Dempsey going into great detail about the feature. I wanted to provide a more concise version of the same information for my future self, as well as a more up-to-date list of upcoming feature flags as of today.
How it works
The way you enable upcoming Swift features in your projects is by passing the -enable-upcoming-feature
flag to the Swift compiler with the name of the feature you’d like to enable.
For example, if you want to enable the InternalImportsByDefault
feature, you’d pass the -enable-upcoming-feature InternalImportsByDefault
flag to the compiler.
Swift Package Manager
As part of the proposal and to ease the adoption of upcoming Swift features, there is a new .enableUpcomingFeature
method you can use on the swiftSettings
property of your Swift package manifest:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "UpcomingFeatures",
products: [
.library(
name: "UpcomingFeatures",
targets: ["UpcomingFeatures"]),
],
dependencies: [
],
targets: [
.target(
name: "UpcomingFeatures",
swiftSettings: [
.enableUpcomingFeature("InternalImportsByDefault")
]
)
]
)
This saves you from having to pass the -enable-upcoming-feature
flag to the compiler manually through the .unsafeFlags
method, which, as the documentation states, makes products that use such unsafe flags ineligible for use by other Swift packages.
Xcode targets
To enable an upcoming Swift feature in an Xcode target, you need to add the -enable-upcoming-feature
string with the feature you’d like to enable to the Other Swift Flags
section of the target’s build settings:
Feature-proofing your work
If you’d like to check whether a feature is available before using it, you can use the hasFeature
compiler directive in your code:
#if hasFeature(UpcomingFeatures)
fileprivate import MyDependency
#else
import MyDependency
#endif
This way, if at some point you decide to no longer adopt a new feature, your code will still compile.
Finding upcoming features
Finding upcoming feature flags is simple, you just need to go to the swift-evolution site and search for any proposal that contains the Upcoming Feature Flag
label in the description:
To save you a bit of time, I have gone through and listed them all here:
- SE-0274:
ConciseMagicFile
. - SE-0286:
ForwardTrailingClosures
. - SE-0335:
ExistentialAny
. - SE-0354:
BareSlashRegexLiterals
. - SE-0384:
ImportObjcForwardDeclarations
. - SE-0401:
DisableOutwardActorInference
. - SE-0409:
InternalImportsByDefault
. - SE-0411:
IsolatedDefaultValues
. - SE-0413:
FullTypedThrows
.
I wanted to provide a small script that would allow you to fetch the full list of upcoming feature flags from the Swift Evolution site’s JSON source file, but unfortunately, and as stated in the site’s JS source code, the upcomingFeatureFlag
property is not yet returned in the proposals.json
file:
/**
* Mapping of proposal ids to upcoming feature flags.
* Temporary until upcomingFeatureFlag property is returned in proposals.json.
*/
const upcomingFeatureFlags = new Map([
['SE-0274', 'ConciseMagicFile'],
['SE-0286', 'ForwardTrailingClosures'],
['SE-0335', 'ExistentialAny'],
['SE-0354', 'BareSlashRegexLiterals'],
['SE-0384', 'ImportObjcForwardDeclarations'],
['SE-0401', 'DisableOutwardActorInference'],
['SE-0409', 'InternalImportsByDefault'],
['SE-0411', 'IsolatedDefaultValues'],
['SE-0413', 'FullTypedThrows'],
])