Meet Swift Stream Ide
Meet Swift Stream IDE: Container-First Swift Development
Are you tired of dealing with broken Swift projects months after you created them? Do you find yourself struggling with setting up the Swift environment, dealing with platform-specific quirks, and suffering from a system cluttered with a bunch of installed dependencies? Or are you just curious about how to try Swift for development outside of the Apple ecosystem?
If so, then read on, the solution has arrived!
TL;DR: a great IDE for hassle-free Swift development, install it from the marketplace.
Swift was released in 2014, and I fell in love with it at first sight. In 2016, its source code was open-sourced on GitHub under the Apache 2.0 license, and a fascinating rush began to use it for pretty much anything beyond macOS and Objective-C.
Many years have passed since then. Today, you can use Swift to write high-performance backends, web applications, firmware for microcontrollers, UI for various platforms, and much more.
But even now, most people consider Swift to be a language purely for Apple development, one that lives solely inside Xcode. The common belief is that to even try it out, you need to buy a Mac.
But what if I told you that’s not the case at all? What if I said Swift lives a life of its own outside of Xcode, and you can write code in it whether you’re on Linux or Windows?
You might have heard before that Swift is worth trying, but I’m guessing you ran into complicated setup instructions. And even if you managed to get it working, you were likely left without a proper environment with syntax highlighting and autocompletion, which immediately killed all the enthusiasm.
My mission is to bring the light: to bypass all the complexities of environment setup and bring you straight to the enjoyable part – working with code.
There’s a good phrase, “let’s give it some love”, and that’s exactly what I decided to do.
To achieve this in 2024, I chose the path of creating an IDE designed to work exclusively inside Docker containers. The goal was one-click setup for a fully-configured environment that stays consistent over time.
This is how the Swift Stream IDE extension for VSCode was born. You might ask: how can an extension be considered an IDE, since an IDE is usually a standalone program? Well, the acronym stands for Integrated Development Environment. VSCode is just a text editor, but if you create an extension that turns this editor into a full-fledged, integrated development environment, then it can rightly be called an IDE.
In this post, we’ll talk about where Swift truly lives today, what you can use it for, and how you can try Swift today using our purpose-built IDE that runs as a VSCode extension.
Is Swift Only Inside Xcode?
That’s a partial truth, largely because Xcode has always been the primary IDE for Swift. Those who have worked with it for years might have warm feelings for it, or they might quietly despise it for everything that’s ever gone wrong: from autocomplete disappearing to memory leaks consuming tens of gigabytes. Nevertheless, there hasn’t been a good, pleasant alternative (AppCode doesn’t count).
Swift itself is distributed as part of a development toolchain, which is officially available for macOS, Windows, and many Linux distributions.
On macOS, people typically install Xcode, which automatically installs Swift, binding its version to that specific Xcode version. Consequently, Swift gets updated alongside Xcode, which is why it feels like the language lives exclusively inside it. However, you can also download the Swift installation package separately from swift.org or use console tools like swiftenv or the new official swiftly.
On alternative operating systems, Swift is always installed separately using a few console commands, as described on swift.org.
Although Swift is a separate entity, and you can install various versions on macOS, if you are exclusively an Xcode lover, you will eventually encounter a situation where, for some mysterious reason, Xcode itself has SwiftPM (the package manager) hardwired to the specific Swift version it came with. This means that even if you switch the toolchain, you simply won’t be able to, for example, fully experiment with a project on Swift 6.3 if your Xcode is for Swift 6.2. But you can do it in the console or in a third-party editor like VSCode.
A development toolchain is a collection of utilities and services that can be used from various editors or manually in the console. Autocomplete and syntax highlighting are implemented through the standard Language Server Protocol (LSP), proving that comfortable life exists outside of Xcode.
Officially, Swift is only supported in Xcode and VSCode. Until 2025, the Swift extension for VSCode was under development by the Swift Server Work Group, but it was unstable and brought significantly less joy to development than Xcode. Since January 2025, a new team from swift.org has taken over the extension, reworked it, and the UX in VSCode has finally caught up with Xcode, in my opinion, it has even become better.
Where is Swift Used Today?
Back in 2016, when Swift was open-sourced and launched on Linux, it opened up new horizons. Many developers started experimenting with it in various areas, such as server-side application development. At that time, I joined the team behind the Vapor server framework and actively worked on developing as many essential libraries as possible to enrich the server-side Swift ecosystem, but that’s a story for another post.
Today, with Swift, we can develop:
- Applications for Apple platforms in Xcode (captain obvious)
- High-performance server applications with Vapor and Hummingbird
- GUI applications for Linux, Windows, and Android
- WebAssembly applications
- Firmware for microcontrollers like ESP32, RPi Pico, STM32, and NRF52
- Libraries and console utilities
…and much more.
While building apps for Apple platforms is straightforward, download Xcode and you’re ready to go, approaching any of the other areas can be quite challenging, even for those on macOS who have been writing Swift for a long time. For each alternative direction, you first need to discover it, then set up the specific environment for it, and learn the initial steps just to create a starter project.
The barrier to entry for Swift is, in my opinion, quite high, especially outside the macOS ecosystem.
Introducing Swift Stream IDE
As I mentioned earlier, the Swift Stream IDE extension for VSCode was created to make life easier for both seasoned programmers who live in the terminal and complete beginners. I particularly want to make Swift interesting and accessible to more people. A larger and more active ecosystem will lead to greater adoption of the language. And that means more beauty in the world, because Swift is both beautiful and concise. And, of course, it will lead to more job opportunities for everyone who works with the language.
Installation
Open the Extensions sidebar in VSCode and start typing Swift Stream.
From the search results, select the extension shown in the screenshot below.
Alternatively, follow the direct link to the marketplace page and install it from there.
Either way, you’ll reach the same installation screen in VSCode where you need to click Install.
Unfortunately, VSCodium lacks the Dev Containers extension, which means Swift Stream IDE won’t work “out of the box” there.
Getting Started
Once the extension is installed, you need to switch to it. In the left sidebar, you’ll find a familiar bird icon (but it is flying upward). Click on it and choose an action.
New Project
When you select Start New Project, you’ll be prompted to enter a project name, a local directory path, and select a project type (stream).
The concept is that Swift Stream IDE supports multiple streams. Simply select the one you need.
On Windows, launch VSCode in WSL2 mode and create your project there. This will prevent any filesystem-related issues.
Each stream comes with its own specific options, which we’ll discuss later.
Existing Project
By selecting Open Existing Project, you can open an existing project. After opening, you’ll need to perform an additional setup by clicking the Setup button.
Only open a folder that contains a
Package.swiftfile.
You’ll be prompted to select your project type, which is crucial for properly configuring the container environment.
If the project is old and wasn’t originally created in Swift Stream IDE, some manual configuration might be needed. You can create a new empty project in the IDE for reference and transfer the environment settings from it. Also, if you run into any issues, feel free to reach out in our Telegram or Discord, we’re here to help you get things working.
System Requirements
Besides VSCode, you’ll need the Dev Containers extension and Docker itself. I wish I could say you just need Docker Engine, but we’re dependent on the requirements of the Dev Containers extension.
Depending on your OS, the requirements differ. On Mac, only Docker Desktop works. On Windows, it’s the same Docker Desktop, though you can choose the Execute in WSL option. On Linux, follow the prompts that appear when creating a project.
On a Mac, for example, even with Docker Desktop pre-installed, you might see a message asking to install Docker Engine. Just click Install, there’s no way around it, the ways of Microsoft are inscrutable..
After clicking the Create Project button, the download of the Docker image will begin. The container will be created from this image, which may take some time depending on your connection. The image size is 350MB+, varying with your chosen project type.
The VSCode window will then reload into container mode and automatically begin downloading the necessary environment components. First, Swift itself will be downloaded (~1GB), which will take some time. Please be patient and take comfort in knowing that none of this will clutter your main system.
When creating other projects with the same Swift version, you won’t need to download anything again, as everything is cached in Docker Volumes and reused. Accordingly, creating a second identical project will be nearly instantaneous.
After the container starts, also wait for the IDE installation to finish.
How It Works
The IDE comes with numerous project templates to choose from.
We welcome your ideas for new project templates. For example, we could add a template featuring your own custom framework.
The base Docker image, swiftstream/base:noble, is built on ubuntu:noble and includes all necessary dependencies for Swift. All other images inherit from this one. This image supports both amd64 and arm64 architectures. You can review the source code for all images in the project repository.
Each child image only includes the essential system dependencies, which are designed to remain static.
The image contains an environment variable, S_IMAGE_VERSION=100, which is read by the IDE for future image updates. If a new image introduces a breaking change, this number is incremented, and the IDE adapts its logic accordingly.
Dynamic dependencies, such as the Swift toolchain itself or various SDKs, are installed by the container’s startup script. This script uses the version specified in the devcontainer.json file, which is initially generated when the project is created. This approach allows the environment to be easily updated without modifying the base image.
All container dependencies (toolchains, SDKs, NDKs, etc.) are installed onto Docker Volumes and mounted into the container.
The benefits of this approach are clear:
- The container itself remains immutable.
- It’s easy to track installed dependencies and remove them all at once if you want to start fresh.
- Containers reuse already installed dependencies, meaning that after the initial setup for the first project, subsequent projects are created instantly.
Nothing is installed on your host machine, your system remains clean.
The IDE’s graphical interface provides a convenient one-click method for switching between Swift versions. This action automatically edits the devcontainer.json file, and all you need to do is approve the container rebuild. You can also edit this file manually. If the new language version is already cached in the Docker Volumes, the rebuild will take just a few seconds.
A Closer Look at devcontainer.json
The central key in this file is S_MODE, which can accept the following values: ANDROID, SERVER, WEB, EMBEDDED, PURE. This key configures the core logic of the entire IDE.
For EMBEDDED mode, for example, there is an additional key, S_EMBEDDED_BRANCH, which can be: RASPBERRY, ESP32, STM32, NRF. This key triggers further adjustments to the interface and logic tailored to the specific microcontroller vendor.
The S_VERSION_[MAJOR | MINOR | PATCH] keys specify the Swift version, which dictates a significant portion of the underlying logic for proper project builds.
Listing all the other key variations would be exhaustive and would resemble formal documentation. You can explore them in your specific devcontainer.json file, and their purpose should be clear from the context.
Within the customizations object, fine-tuning for VSCode takes place, ranging from the installation of necessary extensions to Swift parameters that ensure correct syntax highlighting and autocompletion.
The capAdd and securityOpt parameters are mandatory for the debugger to function correctly.
You might also be interested in the mounts object, which lists all the Docker Volumes mounted to the container. You’ll see standard sections like swift-toolchains and swift-sdks, it’s best not to modify these, as it could break the IDE’s logic. You’ll also find that the project’s .build folder is mounted as a Docker Volume. This is critical for fast project compilation; otherwise, performance drops catastrophically if your host system is not Linux.
You can easily mount anything from your host system by either manually editing this file or using the convenient UI provided in the IDE.
Project Types
Each stream comes with its own refined UI, additional services, and features.
Server
You can create a server application project using either the Vapor or Hummingbird frameworks.
The basic server interface provides options for selecting the target, building, running, testing, enabling hot reload, and choosing the build mode (more on that below).
The build mode determines the format of the final executable. Standard - The build uses glibc, and the resulting binary depends on the system libraries installed on the target machine. Static Linux - The build uses musl, and the executable includes all necessary dependencies, allowing it to run on virtually any Linux system, even without the libraries installed.
Server features add convenience for development or deployment. For example, you can install Nginx in the container with one click to debug your server working with Nginx. You can also install Ngrok to test your server over the internet. Or, you can deploy your project to fly.io in just a couple of clicks. We plan to add more hosting providers like Heroku, Azure, DigitalOcean, Vercel, Alibaba, and possibly Yandex. Let us know in the comments which hosting provider you’d like to see. An SFTP feature for quickly uploading binaries to a server is currently in development.
After adding a feature, you will be prompted to rebuild the container. Click Rebuild.
During the rebuild, layers containing the features are installed on top of the original server image, resulting in a composite container with everything you need.
After installation, corresponding items are added to the side menu.
Nginx allows you to restart the service, displays and lets you change the port, and the Configuration button opens the configuration file in the editor.
In Ngrok, the Start button launches the service in the console, and Configuration also opens its config file in the editor.
For Fly.io, you first need to click Setup. This creates a folder of the same name in the project root, containing a Dockerfile and configuration files for subsequent deployment.
Removing a feature is as easy as installing one, just click Deintegrate.
Web
Swift compiles to WebAssembly, which opens up the possibility of creating web applications. The IDE offers two templates for projects built on the SwifWeb framework: PWA and SPA.
A PWA application will have an additional Service Worker target, while an SPA will not.
The IDE interface for web development has more specific elements.
Build compiles the project into the DevPublic folder, which is locally hosted through a properly configured nginx server on port 77xx.
Debug in Chrome launches a debugging session in a separate Google Chrome instance.
Run Crawl Server starts a server instance for search engines, allowing you to verify how your pages are rendered for them.
We also have flags to enable gzip and brotli compression, and automatic page reloading in development mode.
The Release section here only has a build button that compiles and places the deployment-ready files into the DistPublic folder.
The Maintenance section allows you to separately recompile JS, CSS, HTML, or copy resources to the DevPublic folder.
We can also see the ports for debug, release, and the search engine server, which can be modified as needed.
The Features section is also different. Currently, two deployment features are available: Firebase and Fly.io.
They can be installed with a single click, requiring a container rebuild afterward.
The Firebase and Fly.io features are very simple to use, with just a couple of buttons each.
Configuration will guide you through the console, running commands in the official CLI. Deploy will send your project directly to the hosting service with a single click.
There is an outdated publication about writing web applications, which can give you an idea of how everything looks. A new, updated publication will be written soon.
Embedded
The IDE supports several platforms: ESP32, STM32, NRF52, and Raspberry Pi Pico.
When you select a platform, you can choose from project templates, such as a simple LED blink or controlling an LED strip. For STM32, templates are also available for displaying a logo on a screen and UART echo.
The development environment is individually tailored for each platform.
This is what the IDE interface looks like for ESP32.
Here you can build the firmware file and flash it.
A particularly noteworthy feature is the ability to debug in a pre-configured Wokwi simulator.
The interfaces for other platforms and usage details will be covered in a separate publication.
Android
Yes, this is not a joke. You can write code for Android using Swift. This is achieved through JNI.
The IDE offers two modes: Application and Library.
Creating an Android library is covered in this publication.
A series of publications about creating full-fledged applications will be available soon.
Package (pure)
I call this mode PURE because it’s about creating packages with pure Swift—nothing extra, which means the interface is also original and clean.
It’s what you’d call a pure experience.
This list of templates replicates the functionality of the swift package init command and, in fact, calls it under the hood.
Project Maintenance
When you need to clear the project cache, restart the LSP (code highlighting service), or download/update dependencies, you can do it via the console or through the IDE’s convenient menu.
Compilation Errors and Warnings
If you’ve ever run the swift build command in the console, you might have noticed that the compiler prints the same warning or error as many times as your CPU has threads. On an M1 processor, that could be 10 times. It’s not a pleasant sight—it literally makes your eyes blur… The IDE solves this problem by displaying clickable compilation warnings and errors in a clean format.
Logs
Project builds and other IDE commands generate logs in the Output window.
The log detail level ranges from Normal, Detailed, Verbose, to Unbearable.
Detailed is set by default, while Unbearable is the most extreme level with maximum detail about every single step.
You can switch the Logging Level either through the UI or in the .vscode/settings.json file.
There is also an option to clear the Output window before each build. This is disabled by default.
Sometimes you need to rebuild the container. This can be done with a specific command in VSCode or through convenient buttons in the UI. Special attention should be paid to rebuilding without cache, which might be necessary in rare cases, such as when you need to update the Docker image.
Host Machine Terminal
You might need access to your host machine’s console without leaving the IDE. There’s a dedicated button in the UI for this.
SSH Key Forwarding
During development, you might need keys for your private repositories. Don’t copy or mount them into the container. It’s best to use the SSH Agent.
The IDE provides convenient buttons and even instructions for this, but you can read more about it here.
Support
Let’s conclude our IDE overview with the Support section, which varies depending on the project type and leads to the documentation and repository of the respective framework.
I want to emphasize that convenience lies in the details. A little here, a little there, and soon you won’t want to return to manual setup—you’ll want to stay productively focused on what matters most: developing your product.
I hope you found this interesting and are eager to try this IDE for yourself.
You can install it in the original VSCode from the marketplace.
This is an open-source project distributed under the MIT license.
You can check out the source code and give it a star in the repository at swiftstream/ide.
You can support the project by sharing a link to this publication with friends and colleagues.
We’d be delighted to welcome you in our Discord and Telegram communities.