How to use a pre-release Swift version with command-line tools

⋅ 6 min read ⋅ Xcode

Table of Contents

In the previous article, How to use a pre-release Swift version in Xcode, we learn how to use different Swift toolchain in Xcode. In the end, we learn that changing the Swift version in Xcode doesn't affect any command-line tools.

Selecting a Swift toolchain in Xcode will affect the Xcode IDE only. It won't affect any command-line tools.

In this article, we will learn how to change the Swift toolchain for command-line tools.

Why

The new release of Xcode always ships with the latest stable version of Swift, e.g., Xcode 12.5 shipped with Swift 5.4, but since Swift is open-sourced, it doesn't need to wait for Xcode to release a new version.

If you want to prepare your app for new Swift's features, you can test it with your current Xcode version with the method in How to use a pre-release Swift version in Xcode. But if you also want to make it work with CI, you want to make your tools, e.g., Fastlane and xcodebuild know about the new toolchain too. And that's what you are going to learn in this article.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Checking current Swift version

Most Xcode command-line tools use Swift toolchains based on the current active Xcode.

You can check the currently selected toolchain like this.

xcrun swift -version

You would get the version that ships with your current active Xcode. In my case, it is Swift 5.4 from Xcode 12.5.

Apple Swift version 5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)
Target: arm64-apple-darwin20.5.0

Example

For demonstration, I have created a new project with new Swift 5.5 features.

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let first: CGFloat = 42
let second: Double = 19
let result = first + second // 1
print(result)
}

func usingNewSwiftFeatures() async -> Date { // 2
return Date();
}
}

<1> [SE-0307] Allow interchangeable use of CGFloat and Double types.
<2> [SE-0296] Async/await.

Trying to build the above code with the Swift 5.4 toolchain will get you compiling errors.

Swift 5.4 doesn't know about Swift 5.5 features.
Swift 5.4 doesn't know about Swift 5.5 features.

Run xcodebuild, which uses the default toolchain from the current active developer directory (Swift 5.4 for Xcode 12.5), will produce the same errors.

error: consecutive declarations on a line must be separated by ';'
func usingNewSwiftFeatures() async -> Date {
^
;
/Users/sarunw/Documents/sarunw-example/example-swift5.5/example-swift5.5/ViewController.swift:21:34: error: 'async' modifier is only valid when experimental concurrency is enabled
func usingNewSwiftFeatures() async -> Date {
^
/Users/sarunw/Documents/sarunw-example/example-swift5.5/example-swift5.5/ViewController.swift:21:40: error: expected declaration
func usingNewSwiftFeatures() async -> Date {
^
/Users/sarunw/Documents/sarunw-example/example-swift5.5/example-swift5.5/ViewController.swift:10:7: note: in declaration of 'ViewController'
class ViewController: UIViewController {
^
/Users/sarunw/Documents/sarunw-example/example-swift5.5/example-swift5.5/ViewController.swift:21:10: error: expected '{' in body of function declaration
func usingNewSwiftFeatures() async -> Date {
^
/Users/sarunw/Documents/sarunw-example/example-swift5.5/example-swift5.5/ViewController.swift:17:28: error: binary operator '+' cannot be applied to operands of type 'CGFloat' and 'Double'
let result = first + second
~~~~~ ^ ~~~~~~
/Users/sarunw/Documents/sarunw-example/example-swift5.5/example-swift5.5/ViewController.swift:17:28: note: overloads for '+' exist with these partially matching parameter lists: (CGFloat, CGFloat), (Date, TimeInterval), (DispatchTime, Double), (DispatchWallTime, Double), (Double, Double)
let result = first + second
^

** BUILD FAILED **

Solutions

There are two ways to change the Swift toolchain for command-line tools.

Toolchain parameter

Every Xcode-related command-line tools come with a toolchain parameter.

xcrun -toolchain NAME|IDENTIFIER
xcodebuild -toolchain NAME|IDENTIFIER

Fastlane which uses xcrun and xcodebuild under the hood also has a toolchain parameter.

fastlane gym --toolchain NAME|IDENTIFIER

TOOLCHAINS environment

If you plan to run multiple commands with the same Swift toolchain, you can set the toolchain argument via environment variable instead. You can do this with the export command. This will only affect the current terminal session.

export TOOLCHAINS=NAME|IDENTIFIER

After export, you can run a command without -toolchain.

xcodebuild -toolchain swift
fastlane gym --toolchain swift

// or

export TOOLCHAINS=swift

xcodebuild
fastlane gym

These two methods are actually the same thing but a different way to inject the toolchain argument.

--toolchain: Specifies which toolchain to use to perform the lookup. If no --toolchain argument is provided, then the toolchain to use will be taken from the TOOLCHAINS environment variable, if present.

Where can I find toolchain name and identifier

In the solutions above, you need to specified NAME or IDENTIFIER of the toolchain. You can find this by opening up the Info.plist file of toolchains you want to reference.

In my case, the toolchain is located at /Library/Developer/Toolchains.

  1. Open up /Library/Developer/Toolchains, and you will see your toolchain sitting there. Mine is swift-5.5-DEVELOPMENT-SNAPSHOT-2021-06-02-a.xctoolchain.

  2. Right-click and select "Show Package Contents".

  3. You will see the Info.plist file. Open it up.

  4. You can find name under the Aliases key and identifier under the Bundle identifier key.

In this case, you can use both swift and org.swift.55202106021a as a toolchain name.

xcodebuild -toolchain swift
xcodebuild -toolchain org.swift.55202106021a

Working with CI

Self-hosted CI

If you have control over the CI server, you need to install the Swift toolchain you want and set the environment variable pointing to that toolchain with the export command.

export TOOLCHAINS=NAME|IDENTIFIER 

// All commands will use the toolchain specified.
fastlane gym
...

CI with pre-installed Swift toolchain

If your CI server with the pre-installed Swift toolchain, just add the export command, and you are good to go.

Cloud-based CI

If you use cloud-based CI services with no control over the infrastructure, you have to download the Swift toolchain programmatically before the building starts.

Your script might look something like this.

curl -O https://swift.org/builds/swift-5.5-branch/xcode/swift-5.5-DEVELOPMENT-SNAPSHOT-2021-06-02-a/swift-5.5-DEVELOPMENT-SNAPSHOT-2021-06-02-a-osx.pkg

sudo installer -pkg swift-5.5-DEVELOPMENT-SNAPSHOT-2021-06-02-a-osx.pkg -target /

export TOOLCHAINS=swift

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Caveat

To submit to the App Store, you must build your app using the version of Swift included within Xcode. So, what we learned today is for testing and prepare your app for the new features only.


Read more article about Xcode or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
A new way to style UIButton with UIButton.Configuration in iOS 15

The first part in the series "What's new in UIKit button". An introduction to a new button configuration, a struct that is shaping and styling your button. You no longer need to subclass UIButton ever again with button configuration.

Next
Preview a device in landscape orientation with previewInterfaceOrientation

New in iOS 15, SwiftUI has finally support preview in landscape orientation. Let's find out how to do it.

← Home