Enable upcoming Swift features

Sponsored
RevenueCat logo
Relax, you can roll back your mobile release

No one is immune from shipping critical bugs to production, but Runway helps you limit the amount of havoc that can cause.

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:

Package.swift
// 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:

Regex.swift
#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:

An image showing what a proposal with an upcoming feature label looks like

To save you a bit of time, I have gone through and listed them all here:

  1. SE-0274: ConciseMagicFile.
  2. SE-0286: ForwardTrailingClosures.
  3. SE-0335: ExistentialAny.
  4. SE-0354: BareSlashRegexLiterals.
  5. SE-0384: ImportObjcForwardDeclarations.
  6. SE-0401: DisableOutwardActorInference.
  7. SE-0409: InternalImportsByDefault.
  8. SE-0411: IsolatedDefaultValues.
  9. 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:

swift-evolution.js
/**
 * 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'],
])