Swift in WebAssembly

Swift is a popular language in the Apple ecosystem. But it is no longer just for iOS apps. Thanks to a recent community-led project, it is possible to compile Swift into WebAssembly destined either for the browser or for a WASI environment.

Available Implementations

The SwiftWasm project compiles Swift to WebAssembly. While this is a community-led project, the stated goal of the project is:

[T]o fully support the WebAssembly target for Swift and to be merged into the upstream repository.

It works like a drop-in replacement to the standard Swift tools.

The RemObjects Elements compiler (commercial license required) may support compiling Swift to WebAssembly, too. We have not tested it.

Usage

SwiftWasm supports macOS and Linux operating systems. In addition to the standard Swift environment, you will need to install the SwiftWasm tools.

Once that is done, the swift tool should report SwiftWasm support:

$ swift --version
SwiftWasm Swift version 5.5 (swiftlang-5.5.0)
Target: arm64-apple-darwin21.1.0

From there, the swift tool works as usual.

One of Fermyon’s engineers has been working on a Spin SDK for Swift. It’s in its early stages.

Optimizing

You may find that optimizing Swift code with wasm-opt (part of Binaryen) can cut down binary size. The hello.wasm compiled below was 9.1M. Running it through wasm-opt -Os cut it down to 5.0M.

Pros and Cons

Things we like:

  • Swift is a great language with many good core features
  • The toolchain for Wasm feels no different than native tools
  • The project documentation is very good

We’re neutral about:

  • The binary sizes can be quite large, so we recommend running wasm-opt on your .wasm files to trim out unused code.

Things we’re not big fans of:

  • There is no Windows support

Example

All of our examples follow a documented pattern using common tools.

Once you have installed SwiftWasm, it is easy to build applications for the Fermyon Platform using Wagi.

The simplest Swift program for Spin (or Wagi) looks like this:

print("content-type: text/plain\n\n")
print("Hello, World!\n")

To compile, set the target to wasm32-unknown-wasi (which ensures that WASI support is enabled):

$ swiftc -target wasm32-unknown-wasi hello.swift -o hello.wasm

This binary can be executed with wasmtime:

$ wasmtime hello.wasm
content-type: text/plain

Hello, World!

To run it as a Spin web application, we can add a spin.toml that looks like this:

spin_version = "1"
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
description = "Hello world app."
name = "spin-hello"
trigger = { type = "http", base = "/" }
version = "1.0.0"

[[component]]
id = "hello"
source = "hello.wasm"
[component.trigger]
route = "/"
executor = { type = "wagi" }

Note that we use the wagi executor.

And then, in the same directory, run spin up. Once the server is running, you can use either curl or a web browser to test it.

$ curl localhost:3000                                       

Hello, World!

When the Swift Spin SDK is generally available, it will be possible to use many Fermyon Spin services like Key Value Store, SQLite Database, and Serverless AI.

Learn More

Here are some great resources: