A Complete Guide to Swift Development on Linux
In this tutorial you’ll discover everything you need to start developing Swift on Linux. You’ll learn about LLDB, using SourceKit-LSP, syntax highlighting and the power of autocomplete. By Jari Koopman.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
A Complete Guide to Swift Development on Linux
15 mins
In October 2018, Apple announced work on Language Server Protocol support for Swift. A month later, a second post introduced SourceKit-LSP.
The news was music for Linux developers’ ears. Each announcement proved a step toward Swift autocomplete working with any editor and platform.
In this tutorial, you’ll take the Language Server Protocol (LSP) for a spin using the Visual Studio Code (VSCode) editor on Linux. You’ll get a fell of using VSCode on Linux, compiling the LSP extensions for VSCode and finally debugging your Swift application using the LLDB debugger.
Getting Started
This tutorial requires a basic understanding of Linux and shell script.
To begin, download and install Swift on your Linux system. Go to the Swift website and download the recommended toolchain for your version of Linux.
As of this writing, Apple only supports Ubuntu, so the tutorial will use that distribution.
Once you’ve downloaded the toolchain, navigate to its directory in a terminal window. Replace FILE
with the name of the file you downloaded:
sudo apt-get install curl clang libicu-dev git libatomic1 libicu60 libxml2 libcurl4 zlib1g-dev libbsd0 tzdata libssl-dev libsqlite3-dev libblocksruntime-dev libncurses5-dev libdispatch-dev -y
mkdir ~/swift
tar -xvzf FILE -C ~/swift
This step installs the required dependencies and unpacks the toolchain to ~/swift
.
To use Swift in your shell, you have to tell your shell where to find it. Open ~/.bashrc
in your favorite editor. Then add the following line, replacing FILE
with the name of the directory you unpacked Swift into:
export PATH="~/swift/FILE/usr/bin:$PATH"
That’s it! You’ve installed Swift on your system. To ensure everything works, close and reopen your terminal. Next type:
swift --version
The output should look similar to the following:
Swift version 5.3-dev (LLVM d59da0cbc0, Swift ff36b5ede8) Target: x86_64-unknown-linux-gnu
Next, download the materials for this project by clicking the Download Materials button at the top or bottom of this page. Unzip the file, then navigate to the Starter/
folder. Next run:
swift run
This will build and run the project.
It’s a simple web application programming interface (API) that can store and retrieve TODOs. Once you see Server starting on http://localhost:8080
, your server is available. You can check it by executing the following
curl http://localhost:8080/
in a new terminal tab. This should return It works!
. If it does, then everything is in place for the next steps.
Time to explore the power of the Language Server Protocol!
Introducing the Language Server Protocol
The Language Server Protocol (LSP) is a protocol created by Microsoft. It allows for standardized communication between a language and an editor or tool so that editors can support all languages with LSP at once. Gone are the days of implementing support for each language separately.
The protocol is like a simplified version of HTTP:
- Each message consists of a header and a content part.
- The header has a required
Content-Length
field for the content size in bytes, as well as an optionalContent-Type
field. - The content uses JSON-RPC to describe the structure of requests, responses and notifications.
For example, imagine you’re editing the following Swift file:
protocol MyProtocol { }
struct MyStruct: My
After you begin typing : My
to conform MyStruct
to MyProtocol
, your editor will send the following textDocument/completion
request to the LSP server:
{
"jsonrpc": "2.0",
"id": 1,
"method": "textDocument/completion", // 1
"params": {
"textDocument": { // 2
"uri": "file://Users/jari/Example.swift"
},
"position": { // 3
"line": 2,
"character": 19
}
}
}
Here’s what the request asks of your LSP server:
-
method
tells the server what kind of command you’re sending. In this case, it’scompletion
. -
textDocument
tells the server what file it should be looking at. -
position
tells the server where exactly in the file it should be looking. Both values are zero-based.
After receiving the request, the LSP server — in your case SourceKit-LSP — will figure out possible completions and send the following response:
{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"label": "MyProtocol", // 1
"kind": 8, // 2
"insertText": "Protocol" // 3
}
]
}
id
links this response to the request. The fields in result
do the following:
-
label
indicates what text to show in the autocomplete window. - The
kind
of result tells the editor what kind of completion it is. This can change behavior.8
indicatesInterface
, which is used for Swift protocols. -
insertText
is the actual text value that will be inserted at the cursor position. In this case,My
is stripped since it’s already typed.
This approach allows the editor to serve autocomplete without it knowing you’re working on Swift files. Likewise, the LSP server will never know what editor you’re using. Pretty sneaky of you!
Combining Swift and the Language Server Protocol
Now that you know the basics of LSP, it’s time to work on your machine. If you don’t have VSCode installed, do so by following the instructions from the VSCode docs.
To install SourceKit-LSP, run the following commands in your Downloads directory:
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - # You can skip this command if you already have node installed.
sudo apt-get install -y nodejs # Same goes for this command.
git clone https://github.com/apple/sourcekit-lsp.git
cd sourcekit-lsp/
These commands install NodeJS and clone the sourcekit-lsp
repo.
To build the sourcekit-lsp
project, run the following command but replace FILE
with the name of your toolchain and YOURUSER
with your username:
swift package update #1
swift build -Xcxx -I/home/YOURUSER/swift/FILE/usr/lib/swift #2
sudo mv .build/x86_64-unknown-linux-gnu/debug/sourcekit-lsp /usr/local/bin #3
These commands do the following:
- Update the Swift packages to the latest versions required.
- Build SourceKit-LSP from the source. The
-I
flag adds an extra directory where the C++ compiler will look for.h
files. - Move the built binary to
/usr/local/bin
so VSCode can use it.
Next, navigate to the Editors/vscode
directory. This directory contains all you need to create a VSCode extension that enables SourceKit-LSP.
To build and enable the extension, run the following command:
npm run createDevPackage && \
code --install-extension out/sourcekit-lsp-vscode-dev.vsix
This uses node
and npm
to create and install the extension into VSCode.
Go ahead and use it! Navigate back to your Todos/Starter
directory and run the following:
rm -rf .build/
code .
This removes the .build
directory because of a bug that causes LSP to crash if one already exists and start VSCode in the current directory.
Finally, update the following setting and restart VSCode. You can find your settings by pressing Control-+ and searching for the name of the setting.
"sourcekit-lsp.toolchainPath": "/home/YOURUSER/swift/FILE"
Run swift build
, and give yourself a pat on the back. You successfully installed SourceKit-LSP for VSCode :]
To check if everything worked, open Sources/App/Controllers/TodoController.swift in VSCode and hover over any of the keywords. VSCode should now show a hover card with the related documentation. Awesome!
Using Autocomplete
Time to write some code, and only some because your new LSP autocomplete will handle the rest :]
Add a new function called create
between the index
and delete
functions:
/// Saves a decoded `Todo` to the database.
func create(_ req: Request) throws -> Future<Todo> {
return try req.content.decode(Todo.self).flatMap { todo in
return todo.save(on: req)
}
}
This creates a new method which creates a new Todo
based on the information in the request.
Sure, you could copy-paste this code in. To fully enjoy your new autocomplete, however, try typing it yourself.
To register your new route, open Sources/App/routes.swift and add the following line between router.get
and router.delete
:
router.post("todos", use: todoController.create)
This registers the function you created as a POST endpoint on the API. To test it, run your program and execute the following curl command:
curl -X POST -H "Content-Type: application/json" -d '{"title": "Debug a swift app on Linux"}' http://localhost:8080/todos
This will respond with:
{
"id":1,
"title":"Debug a swift app on Linux"
}
If you see the following JSON
returned you know everything is working!
Debugging Swift on Linux
Being able to create, build and run your Swift apps on Linux is great. No doubt! But what if some nasty bug wiggles its way into your code? Well, then you’ll need to do some debugging.
It’s time to dive into how you can set up LLDB on Linux. You can use LLDB to debug in your terminal or in VSCode.
LLDB is the default debugger used by Xcode. It can be used to debug C, Objective-C, C++ and Swift programs. LLDB comes packaged with the Swift toolchain, meaning you already have it installed and ready to go.
From here on, it’s simple to set up some breakpoints. First, you’ll set up these breakpoints using the LLDB command line. Afterward, you’ll install a VSCode Extension to enable breakpoints inside VSCode.
Again in your Todos/Starter folder, build and run:
lldb .build/debug/Run
This command starts the LLDB session. Note that your program is not started yet. This way, you can set breakpoints beforehand.
In your LLDB session, set a breakpoint in your routes.swift on line 48 like this:
breakpoint set -f routes.swift -l 48
LLDB will tell you where it set the breakpoint.
Next, type r
to run your program. You should immediately hit the breakpoint you set. This allows you to inspect the surrounding variables and execution state.
Running the following:
po router
Will result in out similar to below:
<EngineRouter: 0x5555562a0e70>
You’ve printed the object description for your router. Awesome :]
To continue your application, simply type c
and press enter. You’ll see the normal project output telling you the migrations are complete and the server has started.
Debugging within VSCode
That’s is cool and all, but now you’ll add an extension that allows you to do all this from within VSCode. The extension is called CodeLLDB.
To install it, navigate to the VSCode extensions pane by pressing Control+Shift+X. Search for CodeLLDB and click install.
To configure VSCode to use LLDB as your Swift debugger, create a new folder in your project root called .vscode. Create two files inside this folder called launch.json and tasks.json.
Put the following contents in your launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb", // 1
"request": "launch",
"name": "Debug",
"program": "${workspaceFolder}/.build/debug/Run", // 2
"args": [],
"cwd": "${workspaceFolder}",
"preLaunchTask": "swift-build" // 3
}
]
}
This JSON tells VSCode the following:
- The type of process this is.
lldb
means VSCode will use CodeLLDB. - The program to use. This is the debug target you built.
- Pre-launch tasks to run before your main debug script is executed—in this case build your app.
In your launch.json, we tell VSCode to execute a swift-build
before launching the debug session. You’ll need to define this task. Open tasks.json and add the following JSON:
{
"version": "2.0.0",
"tasks": [
{
"label": "swift-build", // 1
"type": "shell", // 2
"command": "swift build" // 3
}
]
}
This task consists of three fields:
-
label
is the identifier from launch.json. -
type
is shell, meaning you’ll run a plain shell command. -
command
is the command you’ll run to ensure you don’t miss changes because you forgot to recompile.
Now open routes.swift in VSCod,e and click to the left of line 48. A red dot will appear indicating you placed a breakpoint. Press F5 to run your app. VSCode will first compile and then debug it.
You’ll hit your breakpoint again, but this time you can use the built-in VSCode debug navigator to continue.
From left to right, these buttons mean the following:
- Continue will continue the execution of the program until the next breakpoint is hit.
- Step over will move the debugger to the next line.
- Step in will go a level deeper — e.g., go into the function body if your breakpoint hits the invocation.
- Step out is the opposite of step in.
- Restart will re-run your debug session.
- Stop will stop the debug session.
Great work!
Where to Go From Here
You can download the final Swift project with VSCode setup using the Download Materials button at the top or bottom of this page.
To learn more about the SourceKit-LSP project or learn how to set it up with other editors, check out the GitHub repo. It’s open source :]
For more information on how to use LLDB, you can follow the LLDB Tutorial or explore CodeLLDB on GitHub.
And now that you’ve set up your Linux environment, you can continue learning with Getting Started with Server-Side Swift with Vapor.
If you have any comments or questions, please join the forums below!
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development — plans start at just $19.99/month! Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.
Learn more