Building with nightly Swift toolchains on macOS

The Swift website provides nightly builds of the Swift compiler (called toolchains) for download. Building with a nightly compiler can be useful if you want to check if a bug has already been fixed on main, or if you want to experiment with upcoming language features such as Embedded Swift, as I’ve been doing lately.

A toolchain is distributed as a .pkg installer that installs itself into /Library/Developer/Toolchains (or the equivalent path in your home directory). After installation, you have several options to select the toolchain you want to build with:

In Xcode

In Xcode, select the toolchain from the main menu (Xcode > Toolchains), then build and/or run your code normally.

Not all Xcode features work with a custom toolchain. For example, playgrounds don’t work, and Xcode will always use its built-in copy of the Swift Package Manager, so you won’t be able to use unreleased SwiftPM features in this way. Also, Apple won’t accept apps built with a non-standard toolchain for submission to the App Store.

On the command line

When building on the command line there are multiple options, depending on your preferences and what tool you want to use.

The TOOLCHAINS environment variable

All of the various Swift build tools respect the TOOLCHAINS environment variable. This should be set to the desired toolchain’s bundle ID, which you can find in the Info.plist file in the toolchain’s directory.

Example (I’m using a nightly toolchain from 2024-03-03 here):

# My normal Swift version is 5.10
$ swift --version
swift-driver version: Apple Swift version 5.10 (swiftlang- clang-1500.3.9.4)

# The nightly toolchain is 6.0-dev
$ export TOOLCHAINS=org.swift.59202403031a
$ swift --version
Apple Swift version 6.0-dev (LLVM 0c7823cab15dec9, Swift 0cc05909334c6f7)

Toolchain name vs. bundle ID

I think the TOOLCHAINS variable is also supposed to accept the toolchain’s name instead of the bundle ID, but this doesn’t work reliably for me. I tried passing:

  • the DisplayName from Info.plist (“Swift Development Snapshot 2024-03-03 (a)”),
  • the ShortDisplayName (“Swift Development Snapshot”; not unique if you have more than one toolchain installed!),
  • the directory name, both with and without the .xctoolchain suffix,

but none of them worked reliably, especially if you have multiple toolchains installed.

In my limited testing, it seems that Swift picks the first toolchain that matches the short name prefix (“Swift Development Snapshot”) and ignores the long name components. For example, when I select “Swift Development Snapshot 2024-03-03 (a)”, Swift picks swift-DEVELOPMENT-SNAPSHOT-2024-01-30-a, presumably because that’s the “first” one (in alphabetical order) I have installed.

My advice: stick to the bundle ID, it works. Here’s a useful command to find the bundle ID of the latest toolchain you have installed (you may have to adjust the path if you install your toolchains in ~/Library instead of /Library):

$ plutil -extract CFBundleIdentifier raw /Library/Developer/Toolchains/swift-latest.xctoolchain/Info.plist

# Set the toolchain to the latest installed:
export TOOLCHAINS=$(plutil -extract CFBundleIdentifier raw /Library/Developer/Toolchains/swift-latest.xctoolchain/Info.plist)

xcrun and xcodebuild

xcrun and xcodebuild respect the TOOLCHAINS variable too. As an alternative, they also provide an equivalent command line parameter named --toolchain. The parameter has the same semantics as the environment variable: you pass the toolchain’s bundle ID. Example:

$ xcrun --toolchain org.swift.59202403031a --find swiftc

Swift Package Manager

SwiftPM also respects the TOOLCHAINS variable, and it has a --toolchains parameter as well, but this one expects the path to the toolchain, not its bundle ID. Example:

$ swift build --toolchain /Library/Developer/Toolchains/swift-latest.xctoolchain

Missing toolchains are (silently) ignored

Another thing to be aware of: if you specify a toolchain that isn’t installed (e.g. because of a typo or because you’re trying to run a script that was developed in a different environment), none of the tools will fail:

  • swift, xcrun, and xcodebuild silently ignore the toolchain setting and use the default Swift toolchain (set via xcode-select).
  • SwiftPM silently ignores a missing toolchain set via TOOLCHAINS. If you pass an invalid directory to the --toolchains parameter, it at least prints a warning before it continues building with the default toolchain.

I don’t like this. I’d much rather get an error if the build tool can’t find the toolchain I told it to use. It’s especially dangerous in scripts.