Introduction to Open Source Swift on Linux

Learn how to use open source Swift to run a Hello, World app on Linux – and how to use the Swift package manager! By Alexis Gallagher.

Leave a rating/review
Save for later

Note: This tutorial has been updated to work with the latest versions of Ubuntu 14.04 and the Swift 3.0 development snapshots as of April 20, 2016. As a result, some dates and version numbers in this tutorial have been updated from the time of the initial relase of Swift on Linux.

Less than a week ago, the Swift world woke up to an early Christmas present — open source Swift — that you can run on Linux!

And there was even a present inside the present: Apple announced new projects and tools intended to make Swift an incredibly practical choice for Linux development. This creates exciting possibilities, such as running Swift on platforms from Linux servers right down to five-dollar Raspberry Pi Zeros, or in the kajillions of other environments that host Linux.

In this tutorial, you’ll set up a Linux environment on your Mac, install Swift, and compile and run some basic Swift examples on Linux. Then you’ll take stock of the new bits offered by Apple, and finish off with a look into into the misty crystal ball of time, and guess what this all means for the future.

This tutorial does not require any previous knowledge of Linux and you don’t even need Linux installed yet – but if you also have Ubuntu running, all the better! All you need is a Mac running at least OS X El Capitan and an interest in open source Swift. Let’s get started!

Installing Swift on Linux

If you’re already running Ubuntu 14.04 LTS or Ubuntu 15.10, either directly or in a VM on Mac or Windows, you can simply follow the instructions on to download and install Swift for Linux and skip to the next section.

If you’re not running one of the above versions of Ubuntu, follow the instructions below to install Virtualbox and Ubuntu on your Mac. This will use less than 1 GB of disk space and confine Linux to a virtual machine — without affecting your current OS X installation.

Installing VirtualBox

VirtualBox is a free, open-source application that lets you run other operating systems as virtual machines in parallel with OS X.

Go to the VirtualBox downloads page, and download the disk image for VirtualBox 5.0.16 or later. Double-click the downloaded DMG and run the VirtualBox.pkg package installer:


Installing and Using Vagrant

Vagrant is a command-line interface to VirtualBox; it can download virtual machine images for you, run provisioning scripts, and coordinate the interaction between OS X and the machine by setting up things like networking and shared folders.

Go to the Vagrant downloads page and download Vagrant for Mac OS X. Double-click the downloaded DMG disk image and run the Vagrant.pkg package installer:


Now you’ll need a Vagrantfile to tell Vagrant exactly which version of Linux you want to use and how to install Swift on that version of Linux.

Create an empty directory in your Documents folder (or wherever makes sense for your workflow) and name it vagrant-swift. Inside that directory, create a file named Vagrantfile and add to it the lines below:

Vagrant.configure(2) do |config|
  ## 1 = ""
config.vm.provision "shell", inline: <<-SHELL
    ## 2
    sudo apt-get --assume-yes install clang libicu-dev
    ## 3
    curl -O
    ## 4
    tar zxf swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a-ubuntu14.04.tar.gz
    ## 5 
    sudo chown -R vagrant:vagrant swift-*
    ## 6
    echo "export PATH=/home/vagrant/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a-ubuntu14.04/usr/bin:\"${PATH}\"" >> .profile
    echo "Swift has successfully installed on Linux"

Now to run your Vagrantfile. Open Terminal on your Mac (found in the /Applications/Utilities folder), change to the vagrant-swift directory that contains the Vagrant file above, then enter the following command to run your script:

vagrant up

Sit back and wait while Vagrant performs the following steps based on your Vagrantfile:

  1. Downloads a disk image of Ubuntu LTS 14.04 (the latest version as published by Canonical, the Ubuntu company, whenever you are running the script). It will only perform this download once and then cache the image for later use. You will likely see an “Attempting to find and install…” message for the Box file, followed by a message about “Adding it directly…”.
  2. Runs the downloaded disk image in virtualization.
  3. Installs Apple’s C compiler clang and the internationalization library libicu, both components which the Swift compiler requires.
  4. Downloads Swift (as distributed by on April 12th, 2016) and uncompresses it.
  5. Adjust some file permissions needed to use the Swift Package Manager.
  6. Configures the default user’s home PATH variable, so it can run the swift binary, REPL, and other tools.

If this completes successfully, the final message will say that “Swift has successfully installed on Linux”.

Next, you’ll need to connect to your Linux virtual machine. In the same vagrant-swift directory, execute the following command:

vagrant ssh

You should now find yourself at an Ubuntu prompt as a user called “vagrant”. To verify that you can access Swift and it’s functioning properly, execute the following command:

swift --version

You should see something like the following:

Swift version 3.0-dev (LLVM 752e1430fc, Clang 3987718dae, Swift 36739f7b57)
Target: x86_64-unknown-linux-gnu

Tada! Swift on Linux. It’s a Christmas miracle! :]

We're pretty sure Tiny Tim would have appreciated open source Swift.

We’re pretty sure Tiny Tim would have appreciated open source Swift.

Compiling a Program

You’ve installed Swift on Linux. Now it’s time to compile and run something.

Of course, hallowed coding tradition dictates that your first program do nothing but say “Hello, world”. And in true developer form, you’ll do it the easiest way possible. :]

Switch to the Linux shell prompt and execute the following command:

cat > helloworld.swift

Now enter the following single line of Swift code:

print("Hello, world")

Press Enter once followed by Ctrl-D to create the file:

Execute ls in the shell to show the directory listing and confirm that you have created a file named helloworld.swift.

Now execute the following command at the shell prompt to invoke the swift compiler and compile your program:

swiftc helloworld.swift
Note: If you get an error while compiling, you may need to update clang:
sudo apt-get install clang-3.6
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.6 100
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.6 100

Thanks to @phildev for pointing this out!

Execute ls -l h* at the shell prompt to see detailed information on all files beginning with h:

-rwxrwxr-x 1 vagrant vagrant 13684 Dec  4 17:55 helloworld
-rw-rw-r-- 1 vagrant vagrant    22 Dec  4 17:55 helloworld.swift

helloworld has x’s in the first column as it’s an executable file. Execute the following command to run helloworld:


You’ll see the following output:

Hello, world

Lightning and thunderbolts! You’ve run Swift on Linux. So easy! :]

But what does this all mean? What can you really do with Swift on Linux? To answer these questions, put the brakes on this runaway freight train of cross-platform, bleeding-edge and technological euphoria, and take a moment to review what it is that Apple has actually released.

What Apple Has Released

Apple has released five categories of software to date:

1) The Swift compiler (source code and executable binaries), debugger, and interactive prompt (i.e., REPL).

This is the heart of the Swift language itself. Apple has released the source code for the the Swift compiler, the same swiftc program that Xcode uses to compile Swift for iOS and Mac apps.

2) The Swift standard library

This is (almost) exactly the same standard library that’s been used since Swift’s release. It defines strings, collection types, protocols, and other fundamental constructs. Have you been yearning to see how the standard library implements a basic type, like Double or Array? Now you can.

But of course, the Swift standard library alone doesn’t get you very far. How many Swift applications have you written where you don’t import UIKit? Probably not very many. The functionality of Foundation, at a minimum, is indispensable for writing useful software (instead of, you know, “Hello, world”, or the five-millionth example of the factorial function). All of this makes it very promising that Apple has also released …

3) Swift Core Libraries

These are brand new, cross-platform, Objective-C-free re-implementations of existing Apple libraries. They provide higher-level functionality such as networking, file system interaction, date and time calculation. A caveat: they’re not quite finished. But that’s not all. Apple has also released …

4) Swift Package Manager

The Swift package manager is a bit like Carthage; it can download dependencies, compile them, and link them together to create libraries and executables. As of April 2016, it is only distributed with the development builds of Swift rather than the more mature release builds. This is why we have downloaded the development build, which contains Swift 3.

5) Other Components

If you browse Apple’s Github page, you can find other components as well: a Swift wrapper for libdispatch (aka, Grand Central Dispatch), notes on the future plans for Swift 3.0, even a parser/renderer for the Markdown dialect CommonMark.

What Apple Hasn’t Released

That’s quite a pile of functionality — but what hasn’t Apple released?

Well, there’s no Xcode, no AppKit and no UIKit. There’s no Core Graphics, no Core Animation, no AVFoundation, nor many of the other familiar core Objective-C libraries. Basically, most of what you need to create beautiful iOS or Mac apps isn’t here yet.

But what Apple has released is quite significant. Consider the Core Libraries project. Why did Apple take the trouble of re-implementing its own mature, battle-tested Foundation libraries?

The fact that the Core Libraries project doesn’t rely on the Objective-C runtime suggests that Apple is creating the underpinnings for Swift to replace Objective-C completely in the long run. And the fact that it’s cross-platform by design suggests Apple is seriously hoping people will use the language for Linux software development — at least for server software, if not for GUI apps.

But the package manager is most significant of all. Before diving into the reasons why, take a quick kick at the tires and see what it does.

Using the Swift Package Manager

The package manager defines a simple, common-sense directory structure for any Swift code that you would like to build into an executable or library. You’ll look at that format using your VirtualBox install of Ubuntu and Vagrant.

First, execute the following command at the shell prompt to create a directory helloworld-project and dive into it:

mkdir helloworld-project && cd helloworld-project

Now execute the following to to create an empty file named Package.swift:

touch Package.swift

The presence of this file indicates that the current directory defines a Swift package.

Now create a directory for source code files with the following command:

mkdir Sources

Next, execute the following to copy your old program, helloworld.swift, into Sources/ with the new name main.swift:

cp ../helloworld.swift Sources/main.swift

The directory structure of helloworld-project now defines the most basic package possible, and should look as follows:

├── Package.swift
└── Sources/
    └── main.swift

Execute the following command to build the package:

swift build

You should see the following:

vagrant@vagrant-ubuntu-trusty-64:~/helloworld-project$ swift build
Compiling Swift Module 'helloworldproject' (1 sources)
Linking .build/debug/helloworld-project

This creates a new, hidden folder for build products — .build. Your directory structure should now look like the following:

├── .build/
│   └── debug/
│       ├── helloworld-project*
│       ├── helloworld-project.o/
│       │   ├── build.db
│       │   ├── helloworld-project/
│       │   │   ├── main.d
│       │   │   ├── main~partial.swiftdoc
│       │   │   ├── main~partial.swiftmodule
│       │   │   ├── main.swiftdeps
│       │   │   ├── master.swiftdeps
│       │   │   └── output-file-map.json
│       │   ├── home/
│       │   │   └── vagrant/
│       │   │       └── helloworld-project/
│       │   │           └── .build/
│       │   │               └── debug/
│       │   ├── llbuild.yaml
│       │   └── Sources/
│       │       └── main.swift.o
│       ├── helloworldproject.swiftdoc
│       └── helloworldproject.swiftmodule
├── Package.swift
└── Sources/
    └── main.swift

You can verify this hidden folder structure by executing ls -aR.

The most important file here is .build/debug/helloworld-project, which is marked with an asterisk above to indicate that it’s executable. This is your executable, which you can run now just by entering it’s name on the command line, like so:

vagrant@vagrant-ubuntu-trusty-64:~/helloworld-project$ .build/debug/helloworld-project
Hello, world

Cool. But what’s all that other gunk in the directories used for?

All of those intermediate build products are indications of the true scope of the package manager’s functionality. Remember — it’s more than just a compiler. In its capacity as a build tool (like make), the package manager is responsible for compiling multiple files required for a single package and linking them together.

In its capacity as a package manager (like Carthage or CocoaPods), it also has the responsibility of linking together multiple packages and fetching dependent packages on the Internet. This implies the package needs some concept of versioning and compatibility (e.g, “Which dependency can meet my needs?”), and of location (e.g., “Where do I get it?”).

For a less-trivial example, take a look at Apple’s own sample project; it’s an app that simulates the dealing of a deck of cards.

Install Git, change to a new directory, and get the top-level package with the following shell commands:

cd ~
sudo apt-get --assume-yes install git
git clone
cd example-package-dealer

Crack open the Apple project and have a look; you’ll see it includes almost nothing:

├── main.swift
├── Package.swift

However, Package.swift is not empty this time; it includes a small snippet of Swift that declares this package depends on version 1 of another package, example-package-deckofplayingcards, which in turn lives at a URL on github, and that package in turn depends on others, and so on.

The net result is that if you call swift build for the example project, you’ll get more than 400 files, as the build tool downloads those other package repositories, compiles them, and links them together.

Apple provides more details on the package manager on the new website. Below is a quick list of notable points about the new package manager:

  • Uses a simple directory structure for package layout and a simple Swift-based manifest file, as opposed to Xcode, which allows any directory structure, but requires a proprietary Xcode project file to track the freeform chaos this implies.
  • Uses semantic versioning, the defacto standard for declaring package versioning and compatibility information
  • Is tightly integrated with Swift’s notion of a module, which Apple uses for Objective-C and has promoted as a standard for C.
  • Overlaps with some of xcodebuild’s functionality.
  • Performs transitive dependency analysis, but doesn’t yet handle dependency conflicts.
  • Builds executable and static libraries, but doesn’t yet handle dynamic libraries.
  • Uses git, but has no support for other version control system yet.
  • Includes a very thoughtful community proposal document, which describing the thinking behind its design.

If you’ve used CocoaPods, Carthage, or other package managers, this will all seem quite familiar. And indeed, technically, it is nothing new. But it is new to Apple, and this is notable. So let us gaze into the mists of time and ask …

What Does All This Mean?

Why does a package manager matter so much? It’s because a package manager is more than just a tool. Package managers tend to be inextricably tied to the technical community surrounding a software ecosystem, since they define how people interact with each other in the fundamental business of helping each other to make software.

Would the Ruby or Node communities be the same without the gem and npm package managers? No. And it’s no exaggeration to say that CocoaPods has brought iOS developers closer together by making it easier to share work and help each other.

Apple has a bit of a reputation for communicating to developers; it’s not known for supporting channels for feedback from developers or encouraging developer interaction. A good example of this is the Radar bug tracking system, which functions like an interplanetary black hole, silently gobbling up information, and leaving behind no trace visible from the external universe.

But a package manager is inevitably a piece of community infrastructure; it’s something other developers will use to interact with each other, not with Apple. So it’s a bit of a departure for Apple to take the lead in promoting such a tool.

It’s also a good sign that the tool looks and feels like a normal package manager that uses semantic versioning, Git, and other conventional tools and patterns which originated outside of Apple. Apple’s even replaced Radar with a new bug tracking system for Swift based on JIRA, which allows developers to actually view known bugs. And there’s also a long document that solicits contributions from the open source community and establishes a process and a community code of conduct.

It really feels like the Swift team has rolled out the welcome mat! I imagine it as an eerily opalescent welcome mat, with hairline grey trim, which magically hovers exactly one centimer above the ground so it never gets dirty. Others may see it differently. :]

Welcome to Swift Open Source!

The package manager feels like a sign of the broader spirit behind this open source release. With this open source release, Apple hasn’t just thrown a bunch of code “over the wall”. Apple’s taken the first steps toward supporting production uses of Swift on Linux. And more than that, they’ve made big steps toward encouraging and welcoming others to help with this effort.

Exiting the Virtual Machine

When you’re done with your virtual machine, just use logout to exit the shell, and call vagrant suspend to freeze it for later use, or vagrant destroy to blow it away completely and free up the disk space.

Where to Go From Here?

In this tutorial, you’ve installed Linux, installed Swift on Linux, compiled code using Swift on Linux, and taken a quick look at Apple’s package management tool. The fact that you can now run Swift on Linux is incredibly interesting, but the fact that Apple seems to be actually encouraging it, and providing technical and community support for it, is awesome! :]

You’ve only had a really quick look at Swift on Linux, but a lot of the new tooling such as the package manager has consequences for Swift on OS X as well; it’s now easier to share code and to build command line tools. It’s hard to imagine this doesn’t at least hint of new directions in Xcode. The new repositories, the exposure of the roadmap and discussion process for future Swift development are promising — and surprising — developments.

There are interesting days to come!

What are your thoughts on this new paradigm for Swift development? Share your thoughts with us below!