Swift on Linux: installation, Hello World and first project

Mascote LinuxPro programando em Swift numa estação Linux, acompanhado do cachorro caramelo cyborg sentado ao lado

Note (2026): Originally published in May 2017 for Swift 3.1 on Ubuntu; this guide has been updated in 22 September 2026 for Swift 6.4.0, installed using the official method with Swiftly, keeping the original URL and historical date.

Want to try Swift on Linux without starting a huge project? Let's install the language using the official swift.org method, run a Hello World, generate a binary, and build a small terminal tool with arguments. The goal is to leave this article with working code and understand the difference between swift, swiftc and Swift Package Manager.

Before installing: the Swift language is not OpenStack Swift

This tutorial is about the programming language. The package called swift in Debian belongs to OpenStack, an object storage system. Don't use apt install swift as a shortcut to install the compiler from this article: always check the package description.

Use a distribution and architecture listed in the official Swift installation options on Linux. The preparation examples below use Ubuntu/Debian and Bash. Check your distribution and architecture before starting:

cat /etc/os-release
uname -m

To start, set aside a folder in your user directory and have internet access. System dependencies require sudo; Swiftly, the toolchain, and compilation commands stay in your user directory and don't require root.

Install Swift 6.4 with Swiftly

Swiftly is the official installer: it downloads the toolchain, verifies its signature, and allows switching between versions. As of the 22/09/2026 check, the official page indicated Swift 6.4.0, released on 15 September 2026. Prepare the basic tools:

sudo apt update && sudo apt install -y ca-certificates curl gnupg tar

Download the installer indicated by the site, the signature, and the project keys. The file name uses the machine's architecture (x86_64 or aarch64) and always points to the current version of Swiftly:

mkdir -p "$HOME/Downloads/swiftly-linuxpro"
cd "$HOME/Downloads/swiftly-linuxpro"

curl -O https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz
curl -O https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz.sig
curl --compressed https://www.swift.org/keys/all-keys.asc | gpg --import -
gpg --verify swiftly-$(uname -m).tar.gz.sig swiftly-$(uname -m).tar.gz

The output must contain Good signature from "Swift Automatic Signing Key. The warning that the key is not certified by a trusted signature is normal when you have just imported it. If BAD signature, appears, stop: do not extract or run the file. With the signature confirmed, run exactly the command from the official page:

tar zxf swiftly-$(uname -m).tar.gz && \
./swiftly init --quiet-shell-followup && \
. "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh" && \
hash -r

The init downloads a little over 1 GB, checks the signature of the toolchain and sets Swift 6.4.0 as the default. At the end, it prints a command apt-get with the dependencies missing from the system. Do not skip this step: on the clean Ubuntu 24.04 we used in the test, swift failed with libncurses.so.6: cannot open shared object file until we installed the indicated list, which was this:

sudo apt-get -y install binutils git unzip zip gnupg2 libc6-dev libcurl4-openssl-dev \
  libedit2 libgcc-13-dev libpython3-dev libstdc++-13-dev libxml2-dev libncurses-dev \
  libz3-dev pkg-config tzdata zlib1g-dev

On another distribution or version, use the list that your swiftly init shows, not this one. Then check:

swift --version
swiftc --version

The output should mention Swift version 6.4.0. The file env.sh adjusts the current session; it init also configures your shell for subsequent terminals. The details of each step are in the official guide Getting Started with Swiftly on Linux.

Basic Hello World: one file and one line

Start outside the installer folder. Create a study directory and a file named hello.swift:

mkdir -p "$HOME/projetos/swift-linuxpro"
cd "$HOME/projetos/swift-linuxpro"
cat > hello.swift <<'SWIFT'
print("Hello, world!")
SWIFT

swift hello.swift

The expected output is:

Hello, world!

Here, print writes a message to standard output. The call swift hello.swift runs the file without you manually managing an executable. It's a good approach for small experiments; you don't need to create a package for each test.

Compile the same program with swiftc

Now generate an executable with a defined name:

swiftc hello.swift -o hello
./hello

The message is the same, but the flow has changed: first you compiled; then you ran it ./hello. When you change the code, run the compiler again to update the binary. The prefix ./ indicates that the file is in the current directory.

A binary compiled on your computer is not automatically compatible with all of Linux. Architecture, libraries, and runtime of the target environment matter. For more controlled distribution, study the Swift SDK for Static Linux; this is a step beyond Hello World.

Create a tool with Swift Package Manager

When the code gains more files and dependencies, use the Swift Package Manager, which comes with the toolchain. Instead of managing everything manually, it organizes the manifest and the compilation. Create a new project inside the study directory:

mkdir OlaLinux
cd OlaLinux
swift package init --name OlaLinux --type executable
swift run OlaLinux

The initial template already prints a greeting. In the Swift 6.4.0 toolchain tested in this review, Package.swift is the manifest and Sources/OlaLinux/OlaLinux.swift is the entry point. Check the structure created by your version before editing files. The official command-line tools guide explains this flow.

Replace the content of Sources/OlaLinux/OlaLinux.swift with the example below. It accepts a name in the terminal and uses LinuxPro when no argument is given:

@main
struct OlaLinux {
    static func saudacao(para pessoa: String) -> String {
        return "Olá, \(pessoa)! Swift está rodando no Linux."
    }

    static func main() {
        let nome = CommandLine.arguments.dropFirst().first ?? "LinuxPro"
        print(saudacao(para: nome))
    }
}
swift run OlaLinux
swift run OlaLinux Nilton

The expected messages are, respectively:

Olá, LinuxPro! Swift está rodando no Linux.
Olá, Nilton! Swift está rodando no Linux.

The marker @main defines the entry point, which calls main(). In this code, let declares a value that will not be reassigned. CommandLine.arguments contains the process arguments; dropFirst() skips the executable name and first gets the next item, if it exists. The operator ?? provides the default value. The function takes a name and returns the message using string interpolation.

To study the syntax more carefully, follow the official tour of the Swift language. For a CLI with options, help, and input validation, the next step is the Swift Argument Parser; we don't need to add dependencies for this first greeting.

Add an automated test

The template also created a tests folder. Replace Tests/OlaLinuxTests/OlaLinuxTests.swift with the example below, which checks the message produced by the function without depending on the terminal output:

import Testing
@testable import OlaLinux

@Test func saudacaoIncluiNome() {
    #expect(OlaLinux.saudacao(para: "Nilton") ==
        "Olá, Nilton! Swift está rodando no Linux.")
}
swift test

The annotation @Test identifies the test and #expect checks the result. If you change the greeting, update the expectation consciously; a failure should prompt investigation, not be hidden. The @testable import allows testing the internal logic of this module.

Generate the release version and run a quick test

Inside the folder containing Package.swift, compile the release configuration and run the result:

swift build -c release
.build/release/OlaLinux Nilton

A simple output test helps detect regressions when modifying this example:

test "$(.build/release/OlaLinux Nilton)" = "Olá, Nilton! Swift está rodando no Linux." && echo "OK"

This is just a basic check of the program, not a complete test suite. In a larger project, separate the logic into testable modules. To publish the project on Git, keep the code and the manifest; the results folder .build/ does not need to go into the repository.

Alternative: try it using Docker

If Docker is already installed, the official Swift image allows testing without installing the toolchain on the host. This example pins the version used in the review and does not need privileged mode:

docker run --rm swift:6.4.0 swift --version

cd "$HOME/projetos/swift-linuxpro"
docker run --rm \
  --mount "type=bind,source=$PWD,target=/work,readonly" \
  -w /work swift:6.4.0 swift hello.swift

The mount is read-only because this command only runs the script. The first call may download the image. To learn the basics of containers, see also the Docker course. Do not carelessly mix build files generated in containers with those on your host.

Editor and common issues

In VS Code, install the Swift extension and open the project folder, not just a loose file. It integrates code navigation, debugging, and testing features; the official setup guide details the environment. The blog also has a guide to VS Code on Ubuntu.

  • swift: command not found: reload the file env.sh from Swiftly, run hash -r in Bash and check command -v swift.
  • error while loading shared libraries: the system dependencies are missing. Install the list that swiftly init showed; the manual installation documentation also lists requirements per system. Do not create random symlinks between library versions.
  • Package.swift not found: run pwd and ls; the package commands must start from the folder OlaLinux.
  • Error involving @main: an executable can only have one entry point. Do not keep a main.swift file with code at the top alongside the file that uses @main.
  • Unexpected version: use type -a swift to find out if another installation comes before Swiftly in the PATH. Don't delete an old toolchain before identifying which path is in use.

The 2017 registry and the next step

The original tutorial used Swift 3.1.1, Ubuntu 16.04, and manual toolchain extraction in /usr/local. That is the historical context, not the recipe recommended today. Manual downloads remain available, but they require choosing the correct toolchain and its dependencies; Swiftly simplifies this management and is the first method indicated by swift.org.

You now have three clear paths: run a file to try it out, use swiftc to generate a binary and use SwiftPM to organize a project. As an exercise, make the greeting take a second argument and handle the absence of that value. Then compare the experience with Python, with the Go on Linux and with our overview of programming languages.