With Xcode 9, Apple introduced XCS Bots, which lets iOS Developers maintain their builds in a built-in CI system with minimal effort. At the moment, XCS only runs on Mac systems, which implies — apps can only be compiled for platforms of the Apple ecosystem iOS, macOS, watchOS and tvOS. But since Swift is open source and also runs on Linux distributions like Ubuntu, XCS lacks the possibility to compile libraries and Command Line Applications for these kind of platforms.

Docker to the rescue.

Docker provides a powerful interface to virtualise your build infrastructure based on your specific needs. Docker is a bit like a virtual machine, but instead of creating the whole virtual machine, it uses the kernel of the host operating system which in turn results in a significant performance boost and reduces the size of the image.

In order to build applications with Xcode Bots, to run on linux distributions you first have to create a Linux image which is capable of the Swift Toolchain.

Building the Dockerimage

If you do not have docker installed on your target system (= where your XCS runs), download and install the Docker Desktop App.

Docker uses a Dockerfile to build an image with all required dependencies. Luckily, Docker Hub already provides one. Head over to Docker Hub to obtain the official Swift Dockerfile. Make sure to choose the Dockerfile which uses your minimum required Swift version. Save it to your SPM project directory.

Assuming you have already configured a Bot which builds your project, add a new post script with the following lines of code. If you do not created one you can follow this tutorial.

Post Trigger Script

/usr/local/bin/docker build \

--no-cache \

-t swift:ubuntu \

-f path/to/Dockerfile "${XCS_SOURCE_DIR}/your-git-folder-name"

After you have built the image, you are now able to run it, providing the tag you created in the previous command. In addition, mount two directories of your host system in your virtual Docker container. One directory containing your SPM project and another one for the linux compiled version of your project. (NOTE: you have to use absolute paths to mount directories).

Add the following code to your post script of the Bot.

/usr/local/bin/docker run \

--rm \

-v "${XCS_SOURCE_DIR}"/main:/main \

-v "${XCS_SOURCE_DIR}/your-git-folder-name-${XCS_INTEGRATION_ID}":/out \

--name "your-git-folder-name-${XCS_INTEGRATION_ID}" \

swift:ubuntu /path/to/your/build-in-docker.sh

At the end I provided a script which is executed right after the Docker container has started to build the project:

build-in-docker.sh

#!/bin/bash cd project

swift build -c release --skip-update

cp -v .build/x86_64-unknown-linux/release/your-binary-name /out

Your compiled binary should now be available in the folder you mounted previously: "${XCS_SOURCE_DIR}/out-${XCS_INTEGRATION_ID}" . Copy the binary to somewhere safe before the integration ends.

Summing up

You can run a docker container on every integration and compile your source code in it. Everytime you run your image, Docker will spawn a new Container for you. Using Post Trigger environment variables in your Paths, will make sure that they will not intefere.