{"id":126,"date":"2017-05-05T19:40:03","date_gmt":"2017-05-05T22:40:03","guid":{"rendered":"https:\/\/www.linuxpro.com.br\/2017\/05\/instalando-swift-no-linux\/"},"modified":"2026-09-22T21:37:47","modified_gmt":"2026-09-23T00:37:47","slug":"instalando-swift-no-linux","status":"publish","type":"post","link":"https:\/\/www.linuxpro.com.br\/en\/2017\/05\/instalando-swift-no-linux\/","title":{"rendered":"Swift on Linux: installation, Hello World and first project"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/09\/swift-v3.webp?v=20260912\" alt=\"Mascote LinuxPro programando em Swift numa esta\u00e7\u00e3o Linux, acompanhado do cachorro caramelo cyborg sentado ao lado\" width=\"1486\" height=\"856\" \/><\/p>\n<div style=\"background:#fff8e1;border-left:4px solid #8b1a1a;padding:.8em 1em;margin:1em 0\"><strong>Note (2026):<\/strong> 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.<\/div>\n<p>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 <code data-no-translation=\"\">swift<\/code>, <code data-no-translation=\"\">swiftc<\/code> and Swift Package Manager.<\/p>\n<p><!-- more --><\/p>\n<h2>Before installing: the Swift language is not OpenStack Swift<\/h2>\n<p>This tutorial is about the programming language. The <a href=\"https:\/\/packages.debian.org\/bookworm\/swift\">package called swift in Debian<\/a> belongs to OpenStack, an object storage system. Don't use <code data-no-translation=\"\">apt install swift<\/code> as a shortcut to install the compiler from this article: always check the package description.<\/p>\n<p>Use a distribution and architecture listed in the official <a href=\"https:\/\/www.swift.org\/install\/linux\/\">Swift installation options on Linux<\/a>. The preparation examples below use Ubuntu\/Debian and Bash. Check your distribution and architecture before starting:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">cat \/etc\/os-release\nuname -m<\/code><\/pre>\n<p>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.<\/p>\n<h2>Install Swift 6.4 with Swiftly<\/h2>\n<p>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 <a href=\"https:\/\/www.swift.org\/install\/linux\/\">official page<\/a> indicated <strong>Swift 6.4.0<\/strong>, released on 15 September 2026. Prepare the basic tools:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">sudo apt update &amp;&amp; sudo apt install -y ca-certificates curl gnupg tar<\/code><\/pre>\n<p>Download the installer indicated by the site, the signature, and the project keys. The file name uses the machine's architecture (<code data-no-translation=\"\">x86_64<\/code> or <code data-no-translation=\"\">aarch64<\/code>) and always points to the current version of Swiftly:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">mkdir -p \"$HOME\/Downloads\/swiftly-linuxpro\"\ncd \"$HOME\/Downloads\/swiftly-linuxpro\"\n\ncurl -O https:\/\/download.swift.org\/swiftly\/linux\/swiftly-$(uname -m).tar.gz\ncurl -O https:\/\/download.swift.org\/swiftly\/linux\/swiftly-$(uname -m).tar.gz.sig\ncurl --compressed https:\/\/www.swift.org\/keys\/all-keys.asc | gpg --import -\ngpg --verify swiftly-$(uname -m).tar.gz.sig swiftly-$(uname -m).tar.gz<\/code><\/pre>\n<p>The output must contain <code data-no-translation=\"\">Good signature from \"Swift Automatic Signing Key<\/code>. The warning that the key is not certified by a trusted signature is normal when you have just imported it. If <code data-no-translation=\"\">BAD signature<\/code>,  appears, stop: do not extract or run the file. With the signature confirmed, run exactly the command from the official page:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">tar zxf swiftly-$(uname -m).tar.gz &amp;&amp; \\\n.\/swiftly init --quiet-shell-followup &amp;&amp; \\\n. \"${SWIFTLY_HOME_DIR:-$HOME\/.local\/share\/swiftly}\/env.sh\" &amp;&amp; \\\nhash -r<\/code><\/pre>\n<p>The <code data-no-translation=\"\">init<\/code> 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 <code data-no-translation=\"\">apt-get<\/code> with the dependencies missing from the system. <strong>Do not skip this step:<\/strong> on the clean Ubuntu 24.04 we used in the test, <code data-no-translation=\"\">swift<\/code> failed with <code data-no-translation=\"\">libncurses.so.6: cannot open shared object file<\/code> until we installed the indicated list, which was this:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">sudo apt-get -y install binutils git unzip zip gnupg2 libc6-dev libcurl4-openssl-dev \\\n  libedit2 libgcc-13-dev libpython3-dev libstdc++-13-dev libxml2-dev libncurses-dev \\\n  libz3-dev pkg-config tzdata zlib1g-dev<\/code><\/pre>\n<p>On another distribution or version, use the list that your <code data-no-translation=\"\">swiftly init<\/code> shows, not this one. Then check:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">swift --version\nswiftc --version<\/code><\/pre>\n<p>The output should mention <code data-no-translation=\"\">Swift version 6.4.0<\/code>. The file <code data-no-translation=\"\">env.sh<\/code> adjusts the current session; it <code data-no-translation=\"\">init<\/code> also configures your shell for subsequent terminals. The details of each step are in the official guide <a href=\"https:\/\/www.swift.org\/install\/linux\/swiftly\/\">Getting Started with Swiftly on Linux<\/a>.<\/p>\n<h2>Basic Hello World: one file and one line<\/h2>\n<p>Start outside the installer folder. Create a study directory and a file named <code data-no-translation=\"\">hello.swift<\/code>:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">mkdir -p \"$HOME\/projetos\/swift-linuxpro\"\ncd \"$HOME\/projetos\/swift-linuxpro\"\ncat &gt; hello.swift &lt;&lt;'SWIFT'\nprint(\"Hello, world!\")\nSWIFT\n\nswift hello.swift<\/code><\/pre>\n<p>The expected output is:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-text\" data-no-translation=\"\">Hello, world!<\/code><\/pre>\n<p>Here, <code data-no-translation=\"\">print<\/code> writes a message to standard output. The call <code data-no-translation=\"\">swift hello.swift<\/code> 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.<\/p>\n<h2>Compile the same program with swiftc<\/h2>\n<p>Now generate an executable with a defined name:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">swiftc hello.swift -o hello\n.\/hello<\/code><\/pre>\n<p>The message is the same, but the flow has changed: first you compiled; then you ran it <code data-no-translation=\"\">.\/hello<\/code>. When you change the code, run the compiler again to update the binary. The prefix <code data-no-translation=\"\">.\/<\/code> indicates that the file is in the current directory.<\/p>\n<p>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 <a href=\"https:\/\/www.swift.org\/documentation\/articles\/static-linux-getting-started.html\">Swift SDK for Static Linux<\/a>; this is a step beyond Hello World.<\/p>\n<h2>Create a tool with Swift Package Manager<\/h2>\n<p>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:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">mkdir OlaLinux\ncd OlaLinux\nswift package init --name OlaLinux --type executable\nswift run OlaLinux<\/code><\/pre>\n<p>The initial template already prints a greeting. In the Swift 6.4.0 toolchain tested in this review, <code data-no-translation=\"\">Package.swift<\/code> is the manifest and <code data-no-translation=\"\">Sources\/OlaLinux\/OlaLinux.swift<\/code> is the entry point. Check the structure created by your version before editing files. The <a href=\"https:\/\/www.swift.org\/getting-started\/cli-swiftpm\/\">official command-line tools guide<\/a> explains this flow.<\/p>\n<p>Replace the content of <code data-no-translation=\"\">Sources\/OlaLinux\/OlaLinux.swift<\/code> with the example below. It accepts a name in the terminal and uses LinuxPro when no argument is given:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-swift\" data-no-translation=\"\">@main\nstruct OlaLinux {\n    static func saudacao(para pessoa: String) -&gt; String {\n        return \"Ol\u00e1, \\(pessoa)! Swift est\u00e1 rodando no Linux.\"\n    }\n\n    static func main() {\n        let nome = CommandLine.arguments.dropFirst().first ?? \"LinuxPro\"\n        print(saudacao(para: nome))\n    }\n}<\/code><\/pre>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">swift run OlaLinux\nswift run OlaLinux Nilton<\/code><\/pre>\n<p>The expected messages are, respectively:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-text\" data-no-translation=\"\">Ol\u00e1, LinuxPro! Swift est\u00e1 rodando no Linux.\nOl\u00e1, Nilton! Swift est\u00e1 rodando no Linux.<\/code><\/pre>\n<p>The marker <code data-no-translation=\"\">@main<\/code> defines the entry point, which calls <code data-no-translation=\"\">main()<\/code>. In this code, <code data-no-translation=\"\">let<\/code> declares a value that will not be reassigned. <code data-no-translation=\"\">CommandLine.arguments<\/code> contains the process arguments; <code data-no-translation=\"\">dropFirst()<\/code> skips the executable name and <code data-no-translation=\"\">first<\/code> gets the next item, if it exists. The operator <code data-no-translation=\"\">??<\/code> provides the default value. The function takes a name and returns the message using string interpolation.<\/p>\n<p>To study the syntax more carefully, follow the <a href=\"https:\/\/docs.swift.org\/swift-book\/documentation\/the-swift-programming-language\/guidedtour\/\">official tour of the Swift language<\/a>. For a CLI with options, help, and input validation, the next step is the <a href=\"https:\/\/github.com\/apple\/swift-argument-parser\">Swift Argument Parser<\/a>; we don't need to add dependencies for this first greeting.<\/p>\n<h2>Add an automated test<\/h2>\n<p>The template also created a tests folder. Replace <code data-no-translation=\"\">Tests\/OlaLinuxTests\/OlaLinuxTests.swift<\/code> with the example below, which checks the message produced by the function without depending on the terminal output:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-swift\" data-no-translation=\"\">import Testing\n@testable import OlaLinux\n\n@Test func saudacaoIncluiNome() {\n    #expect(OlaLinux.saudacao(para: \"Nilton\") ==\n        \"Ol\u00e1, Nilton! Swift est\u00e1 rodando no Linux.\")\n}<\/code><\/pre>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">swift test<\/code><\/pre>\n<p>The annotation <code data-no-translation=\"\">@Test<\/code> identifies the test and <code data-no-translation=\"\">#expect<\/code> checks the result. If you change the greeting, update the expectation consciously; a failure should prompt investigation, not be hidden. The <code data-no-translation=\"\">@testable import<\/code> allows testing the internal logic of this module.<\/p>\n<h2>Generate the release version and run a quick test<\/h2>\n<p>Inside the folder containing <code data-no-translation=\"\">Package.swift<\/code>, compile the release configuration and run the result:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">swift build -c release\n.build\/release\/OlaLinux Nilton<\/code><\/pre>\n<p>A simple output test helps detect regressions when modifying this example:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">test \"$(.build\/release\/OlaLinux Nilton)\" = \"Ol\u00e1, Nilton! Swift est\u00e1 rodando no Linux.\" &amp;&amp; echo \"OK\"<\/code><\/pre>\n<p>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 <code data-no-translation=\"\">.build\/<\/code> does not need to go into the repository.<\/p>\n<h2>Alternative: try it using Docker<\/h2>\n<p>If Docker is already installed, the <a href=\"https:\/\/www.swift.org\/install\/linux\/docker\/\">official Swift image<\/a> allows testing without installing the toolchain on the host. This example pins the version used in the review and does not need privileged mode:<\/p>\n<pre data-no-translation=\"\"><code class=\"language-bash\" data-no-translation=\"\">docker run --rm swift:6.4.0 swift --version\n\ncd \"$HOME\/projetos\/swift-linuxpro\"\ndocker run --rm \\\n  --mount \"type=bind,source=$PWD,target=\/work,readonly\" \\\n  -w \/work swift:6.4.0 swift hello.swift<\/code><\/pre>\n<p>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 <a href=\"\/en\/2017\/08\/curso-de-docker-gratis\/\">the Docker course<\/a>. Do not carelessly mix build files generated in containers with those on your host.<\/p>\n<h2>Editor and common issues<\/h2>\n<p>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 <a href=\"https:\/\/www.swift.org\/documentation\/articles\/getting-started-with-vscode-swift.html\">official setup guide<\/a> details the environment. The blog also has a guide to <a href=\"\/en\/2017\/05\/instalando-visualstudio-no-ubuntu\/\">VS Code on Ubuntu<\/a>.<\/p>\n<ul>\n<li><strong>swift: command not found:<\/strong> reload the file <code data-no-translation=\"\">env.sh<\/code> from Swiftly, run <code data-no-translation=\"\">hash -r<\/code> in Bash and check <code data-no-translation=\"\">command -v swift<\/code>.<\/li>\n<li><strong>error while loading shared libraries:<\/strong> the system dependencies are missing. Install the list that <code data-no-translation=\"\">swiftly init<\/code> showed; the <a href=\"https:\/\/www.swift.org\/install\/linux\/tarball\/\">manual installation documentation<\/a> also lists requirements per system. Do not create random symlinks between library versions.<\/li>\n<li><strong>Package.swift not found:<\/strong> run <code data-no-translation=\"\">pwd<\/code> and <code data-no-translation=\"\">ls<\/code>; the package commands must start from the folder <code data-no-translation=\"\">OlaLinux<\/code>.<\/li>\n<li><strong>Error involving @main:<\/strong> an executable can only have one entry point. Do not keep a <code data-no-translation=\"\">main.swift<\/code> file with code at the top alongside the file that uses <code data-no-translation=\"\">@main<\/code>.<\/li>\n<li><strong>Unexpected version:<\/strong> use <code data-no-translation=\"\">type -a swift<\/code> 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.<\/li>\n<\/ul>\n<h2>The 2017 registry and the next step<\/h2>\n<p>The original tutorial used Swift 3.1.1, Ubuntu 16.04, and manual toolchain extraction in <code data-no-translation=\"\">\/usr\/local<\/code>. 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.<\/p>\n<p>You now have three clear paths: run a file to try it out, use <code data-no-translation=\"\">swiftc<\/code> 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 <a href=\"\/en\/2017\/04\/python-do-basico-ao-profissional\/\">Python<\/a>, with the <a href=\"\/en\/2017\/05\/instalando-golang-no-linux\/\">Go on Linux<\/a> and with our <a href=\"\/en\/2017\/05\/linguagens-de-programacao\/\">overview of programming languages<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>Want to try Swift on Linux without starting a huge project? Let's install the language using the official method from swift.org, run a Hello World, generate a binary, and put together a small terminal tool with arguments. The goal is to leave \u2026 <a title=\"Swift on Linux: installation, Hello World and first project\" class=\"read-more\" href=\"https:\/\/www.linuxpro.com.br\/en\/2017\/05\/instalando-swift-no-linux\/\" aria-label=\"Read more about Swift no Linux: instala\u00e7\u00e3o, Hello World e primeiro projeto\">Read more<\/a><\/p>","protected":false},"author":0,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,66,3],"tags":[4,67,5],"class_list":["post-126","post","type-post","status-publish","format-standard","hentry","category-linux","category-swift","category-ubuntu","tag-linux","tag-swift","tag-ubuntu"],"_links":{"self":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts\/126","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/comments?post=126"}],"version-history":[{"count":7,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts\/126\/revisions"}],"predecessor-version":[{"id":1625,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts\/126\/revisions\/1625"}],"wp:attachment":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/media?parent=126"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/categories?post=126"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/tags?post=126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}