├── .github ├── FUNDING.yml └── workflows │ └── deploy.yml ├── .gitignore ├── book.toml └── src ├── README.md ├── SUMMARY.md ├── contribution-guide ├── how-to-build-toolchain.md └── index.md ├── examples ├── example-projects.md ├── exporting-function.md ├── importing-function.md └── index.md └── getting-started ├── browser-app.md ├── chrome-devtools-swift.png ├── chrome-devtools.png ├── concurrency.md ├── coverage-support.png ├── debugging.md ├── hello-world.md ├── index.md ├── javascript-interop.md ├── multithreading.md ├── porting.md ├── setup-snapshot.md ├── setup.md ├── swift-package.md ├── testing.md ├── troubleshooting.md ├── vscode-editing.png └── vscode.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [swiftwasm, carson-katri, kateinoigakukun, MaxDesiatov] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: swiftwasm # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Github Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Setup mdBook 15 | uses: peaceiris/actions-mdbook@v1 16 | with: 17 | mdbook-version: '0.4.3' 18 | - run: mdbook build 19 | 20 | - name: Deploy 21 | uses: peaceiris/actions-gh-pages@v3 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | publish_dir: ./book 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Yuta Saito"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Swift and WebAssembly" 7 | 8 | 9 | [output.html] 10 | cname = "book.swiftwasm.org" 11 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Welcome to the SwiftWasm Documentation! 4 | 5 | [SwiftWasm is an open source project to support the WebAssembly target for Swift.](https://github.com/swiftwasm) 6 | 7 | The goal of this project is to fully support the WebAssembly target for [Swift](https://swift.org) and to be merged into [the upstream repository](https://github.com/apple/swift). 8 | 9 | WebAssembly is described on its [home page](https://webassembly.org/) as: 10 | 11 | > WebAssembly (abbreviated as Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications. 12 | 13 | 14 | We use LLVM as a compiler backend to produce WebAssembly binaries. Our resulting binaries also depend on [WASI](https://wasi.dev), which is a modular system interface for WebAssembly. WASI is mainly required to compile Swift Standard Library. 15 | 16 | 17 | ### Important note 18 | In 2024, Apple introduced [Swift Embedded](https://github.com/swiftlang/swift/blob/main/docs/EmbeddedSwift/UserManual.md). 19 | While both projects benefit from each other, it is important to understand that they are different targets at the build phase, consequentially with different sets of abilities. 20 | Embedded Swift [very limited](https://github.com/swiftlang/swift/blob/main/docs/EmbeddedSwift/EmbeddedSwiftStatus.md#embedded-swift-language-features) but can produce small binaries. [Example](https://github.com/apple/swift-for-wasm-examples). 21 | 22 | 23 | 24 | ## Project Status 25 | 26 | [All compiler and standard library changes have been upstreamed to the official Swift repository, and the upstream CI is now testing WebAssembly targets.](https://forums.swift.org/t/stdlib-and-runtime-tests-for-wasm-wasi-now-available-on-swift-ci/70385) 27 | 28 | The remaining works are: 29 | 30 | - Integrating the build system with the official Swift CI. 31 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](README.md) 4 | - [Getting Started](getting-started/index.md) 5 | - [Installation](getting-started/setup.md) 6 | - [Porting code](getting-started/porting.md) 7 | - [Creating a browser app](getting-started/browser-app.md) 8 | - [JavaScript interoperation](getting-started/javascript-interop.md) 9 | - [Concurrency](getting-started/concurrency.md) 10 | - [Multithreading](getting-started/multithreading.md) 11 | - [Testing your app](getting-started/testing.md) 12 | - [Visual Studio Code](./getting-started/vscode.md) 13 | - [Debugging](getting-started/debugging.md) 14 | - [Troubleshooting](getting-started/troubleshooting.md) 15 | - [Examples](examples/index.md) 16 | - [Importing function](examples/importing-function.md) 17 | - [Exporting function](examples/exporting-function.md) 18 | - [Example projects](examples/example-projects.md) 19 | - [Contribution Guide](contribution-guide/index.md) 20 | - [How to build the toolchain](contribution-guide/how-to-build-toolchain.md) 21 | 22 | -------------------------------------------------------------------------------- /src/contribution-guide/how-to-build-toolchain.md: -------------------------------------------------------------------------------- 1 | # How to build toolchain 2 | 3 | This document describes how to build the toolchain for WebAssembly. 4 | This is just a quick guide, so if you want to know more about the toolchain, it might be good entry point to read [continuous integration scripts](https://github.com/swiftwasm/swiftwasm-build/blob/main/.github/workflows/build-toolchain.yml). 5 | Or you can ask questions in GitHub issues or SwiftWasm Discord server (see [the official website](https://swiftwasm.org) for the link). 6 | 7 | ## 1. Checkout the project source code. 8 | 9 | ```sh 10 | $ mkdir swiftwasm-source 11 | $ cd swiftwasm-source 12 | $ git clone https://github.com/swiftwasm/swiftwasm-build.git 13 | $ cd swiftwasm-build 14 | $ ./tools/build/install-build-sdk.sh main 15 | $ ./tools/git-swift-workspace --scheme main 16 | ``` 17 | 18 | ## 2. Install required dependencies 19 | 20 | 1. [Please follow the upstream instruction](https://github.com/apple/swift/blob/main/docs/HowToGuides/GettingStarted.md#installing-dependencies) 21 | 2. (If you want to run test suite) Install [`Wasmtime`](https://wasmtime.dev/) 22 | 23 | If you are using macOS, please ensure that you don't have `llvm` package installed via Homebrew. 24 | 25 | ## 3. Build the toolchain 26 | 27 | `./tools/build/build-toolchain.sh` 28 | 29 | This script will build the following components: 30 | 31 | 1. Swift compiler that can compile Swift code to WebAssembly support 32 | 2. Swift standard library and core libraries for WebAssembly 33 | 34 | 35 | ## Build on Docker 36 | 37 | You can also build the toolchain on Docker image used in CI. 38 | Note that you have already checked out the source code in [the previous step](#1-checkout-the-project-source-code). 39 | 40 | ```sh 41 | $ docker volume create oss-swift-package 42 | $ docker run --name swiftwasm-ci-buildbot \ 43 | -dit \ 44 | -w /home/build-user/ \ 45 | -v ./swiftwasm-source:/source \ 46 | -v oss-swift-package:/home/build-user \ 47 | ghcr.io/swiftwasm/swift-ci:main-ubuntu-20.04 48 | $ docker exec swiftwasm-ci-buildbot /bin/bash -lc 'env; cp -r /source/* /home/build-user/; ./swiftwasm-build/tools/build/ci.sh main' 49 | $ docker cp swiftwasm-ci-buildbot:/home/build-user/swift-wasm-DEVELOPMENT-SNAPSHOT-*-ubuntu-20.04.tar.gz . 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /src/contribution-guide/index.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | ## Repositories 4 | 5 | ### [swiftwasm/swiftwasm-build](https://github.com/swiftwasm/swiftwasm-build) 6 | 7 | The main development repository for the SwiftWasm project. It contains the build script and patches for building the Swift compiler and standard library for WebAssembly. See the [README](https://github.com/swiftwasm/swiftwasm-build/blob/main/README.md) for more information. 8 | 9 | ### [swiftwasm/icu4c-wasi](https://github.com/swiftwasm/icu4c-wasi) 10 | 11 | Build script and patches for building ICU project for WebAssembly. [The required changes to build 12 | it](https://github.com/unicode-org/icu/pull/990) were merged to the upstream repository. 13 | -------------------------------------------------------------------------------- /src/examples/example-projects.md: -------------------------------------------------------------------------------- 1 | ## Example Projects 2 | 3 | 4 | You can learn more practical usage of our toolchain in [swiftwasm/awesome-swiftwasm](https://github.com/swiftwasm/awesome-swiftwasm) -------------------------------------------------------------------------------- /src/examples/exporting-function.md: -------------------------------------------------------------------------------- 1 | # Exporting function for host environment 2 | 3 | ## Swift 6.0 or later 4 | 5 | If you use Swift 6.0 or later, you can use `@_expose(wasm, "add")` and omit the `--export` linker flag. 6 | 7 | ```swift 8 | // File name: lib.swift 9 | @_expose(wasm, "add") 10 | @_cdecl("add") // This is still required to call the function with C ABI 11 | func add(_ lhs: Int, _ rhs: Int) -> Int { 12 | return lhs + rhs 13 | } 14 | ``` 15 | 16 | Then you can compile the Swift code with the following command without `--export` linker flag. 17 | 18 | ```bash 19 | $ swiftc \ 20 | -target wasm32-unknown-wasi \ 21 | -parse-as-library \ 22 | lib.swift -o lib.wasm \ 23 | -Xclang-linker -mexec-model=reactor 24 | ``` 25 | 26 | ## Swift 5.10 or earlier 27 | 28 | You can expose a Swift function for host environment using special attribute and linker option. 29 | 30 | ```swift 31 | // File name: lib.swift 32 | @_cdecl("add") 33 | func add(_ lhs: Int, _ rhs: Int) -> Int { 34 | return lhs + rhs 35 | } 36 | ``` 37 | 38 | You need to compile the Swift code with linker option `--export`. 39 | 40 | To call the exported function as a library multiple times, you need to: 41 | 42 | 1. Compile it as a [*WASI reactor* execution model](https://github.com/WebAssembly/WASI/blob/bac366c8aeb69cacfea6c4c04a503191bf1cede1/legacy/application-abi.md). 43 | The default execution model is *command*, so you need to pass `-mexec-model=reactor` to linker. 44 | 2. Call `_initialize` function before interacting with the instance. 45 | 46 | ```bash 47 | $ swiftc \ 48 | -target wasm32-unknown-wasi \ 49 | -parse-as-library \ 50 | lib.swift -o lib.wasm \ 51 | -Xlinker --export=add \ 52 | -Xclang-linker -mexec-model=reactor 53 | ``` 54 | 55 | Then, you can use the exported function from host environment. 56 | 57 | ```javascript 58 | // File name: main.mjs 59 | import { WASI, File, OpenFile, ConsoleStdout } from "@bjorn3/browser_wasi_shim"; 60 | import fs from "fs/promises"; 61 | 62 | // Instantiate a new WASI Instance 63 | // See https://github.com/bjorn3/browser_wasi_shim/ for more detail about constructor options 64 | let wasi = new WASI([], [], 65 | [ 66 | new OpenFile(new File([])), // stdin 67 | ConsoleStdout.lineBuffered(msg => console.log(`[WASI stdout] ${msg}`)), 68 | ConsoleStdout.lineBuffered(msg => console.warn(`[WASI stderr] ${msg}`)), 69 | ], 70 | { debug: false } 71 | ); 72 | 73 | const wasmBinary = await fs.readFile("lib.wasm"); 74 | 75 | // Instantiate the WebAssembly file 76 | const { instance } = await WebAssembly.instantiate(wasmBinary, { 77 | wasi_snapshot_preview1: wasi.wasiImport, 78 | }); 79 | // Initialize the instance by following WASI reactor ABI 80 | wasi.initialize(instance); 81 | // Get the exported function 82 | const addFn = instance.exports.add; 83 | console.log("2 + 3 = " + addFn(2, 3)) 84 | ``` 85 | 86 | If you use SwiftPM package, you can omit linker flag using clang's `__atribute__`. Please see [swiftwasm/JavaScriptKit#91](https://github.com/swiftwasm/JavaScriptKit/pull/91/files) for more detail info 87 | 88 | -------------------------------------------------------------------------------- /src/examples/importing-function.md: -------------------------------------------------------------------------------- 1 | # Importing a function from host environments 2 | 3 | ## Swift 6.0 or later 4 | 5 | If you are using Swift 6.0 or later, you can use experimental `@_extern(wasm)` attribute 6 | 7 | Swift 6.0 introduces a new attribute `@_extern(wasm)` to import a function from the host environment. 8 | To use this experimental feature, you need to enable it in your SwiftPM manifest file: 9 | 10 | ```swift 11 | .executableTarget( 12 | name: "Example", 13 | swiftSettings: [ 14 | .enableExperimentalFeature("Extern") 15 | ]), 16 | ``` 17 | 18 | Then, you can import a function from the host environment as follows without using C headers: 19 | 20 | ```swift 21 | @_extern(wasm, module: "env", name: "add") 22 | func add(_ a: Int, _ b: Int) -> Int 23 | 24 | print(add(2, 2)) 25 | ``` 26 | 27 | ## Swift 5.10 or earlier 28 | 29 | You can import a function from your host environment using the integration of Swift Package Manager 30 | with C targets. Firstly, you should declare a signature for your function in a C header with an 31 | appropriate `__import_name__` attribute: 32 | 33 | ```c 34 | __attribute__((__import_name__("add"))) 35 | extern int add(int lhs, int rhs); 36 | ``` 37 | 38 | Here `__import_name__` specifies the name under which this function will be exposed to Swift code. 39 | Move this C header to a separate target, we'll call it `HostFunction` in this example. Your 40 | `Package.swift` manifest for your WebAssembly app would look like this: 41 | 42 | ```swift 43 | // swift-tools-version:5.9 44 | import PackageDescription 45 | 46 | let package = Package( 47 | name: "Example", 48 | targets: [ 49 | .target(name: "HostFunction", dependencies: []), 50 | .executableTarget(name: "Example", dependencies: ["HostFunction"]), 51 | ] 52 | ) 53 | ``` 54 | 55 | Place this header into the `include` subdirectory of your `HostFunction` target directory. You can 56 | then import this host function module into Swift code just as any other module: 57 | 58 | ```swift 59 | import HostFunction 60 | 61 | print(add(2, 2)) 62 | ``` 63 | 64 | Then, you can inject a host function into the produced WebAssembly binary. 65 | 66 | Note that we use `env` as default import module name. You can specify the module name as 67 | `__import_module__` in your C header. The full list of attributes in the header could look 68 | like `__attribute__((__import_module__("env"),__import_name__("add")))`. 69 | 70 | ```javascript 71 | // File name: main.mjs 72 | import { WASI, File, OpenFile, ConsoleStdout } from "@bjorn3/browser_wasi_shim"; 73 | import fs from "fs/promises"; 74 | 75 | const main = async () => { 76 | 77 | // Instantiate a new WASI Instance 78 | // See https://github.com/bjorn3/browser_wasi_shim/ for more detail about constructor options 79 | let wasi = new WASI([], [], 80 | [ 81 | new OpenFile(new File([])), // stdin 82 | ConsoleStdout.lineBuffered(msg => console.log(`[WASI stdout] ${msg}`)), 83 | ConsoleStdout.lineBuffered(msg => console.warn(`[WASI stderr] ${msg}`)), 84 | ], 85 | { debug: false } 86 | ); 87 | 88 | const wasmBinary = await fs.readFile(".build/wasm32-unknown-wasi/debug/Example.wasm"); 89 | 90 | // Instantiate the WebAssembly file 91 | let { instance } = await WebAssembly.instantiate(wasmBinary, { 92 | wasi_snapshot_preview1: wasi.wasiImport, 93 | env: { 94 | add: (lhs, rhs) => (lhs + rhs), 95 | } 96 | }); 97 | 98 | wasi.start(instance); 99 | }; 100 | 101 | main() 102 | ``` 103 | 104 | If you use Go bindings for Wasmer as your host environment, you should check [an example 105 | repository](https://github.com/hassan-shahbazi/swiftwasm-go) from one of our contributors that shows 106 | an integration with an imported host function. 107 | 108 | A more streamlined way to import host functions will be implemented in the future version of the 109 | SwiftWasm toolchain. 110 | 111 | 112 | -------------------------------------------------------------------------------- /src/examples/index.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This section shows you example usage of our toolchain. -------------------------------------------------------------------------------- /src/getting-started/browser-app.md: -------------------------------------------------------------------------------- 1 | # Creating a Browser App 2 | 3 | Currently, [the Tokamak UI framework](https://tokamak.dev) is one of the easiest ways to build a 4 | browser app with SwiftWasm. It tries to be compatible with [the SwiftUI 5 | API](https://developer.apple.com/xcode/swiftui/) as much as possible, which potentially allows 6 | you to share most if not all code between SwiftWasm and other platforms. 7 | 8 | ## Requirements 9 | 10 | Tokamak relies on [the `carton` development tool](https://carton.dev) for development and testing. 11 | While you can build Tokamak apps without `carton`, that would require a lot of manual steps that are 12 | already automated with `carton`. 13 | 14 | ### System Requirements 15 | 16 | - [Swift 5.9.2](https://swift.org/download/) 17 | 18 | >Important: 19 | >Tokamak UI currently is not compatible with swift 6.0+. 20 | 21 | ### Installation 22 | 23 | 1. Create a directory for your project and make it current: 24 | 25 | ``` 26 | mkdir MyApp && cd MyApp 27 | ``` 28 | 29 | 2. Initialize the project: 30 | 31 | ``` 32 | swift package init --type executable 33 | ``` 34 | 35 | 3. Add Tokamak and carton as dependencies to your `Package.swift`: 36 | 37 | ```swift 38 | // swift-tools-version:5.8 39 | import PackageDescription 40 | let package = Package( 41 | name: "MyApp", 42 | platforms: [.macOS(.v11), .iOS(.v13)], 43 | dependencies: [ 44 | .package(url: "https://github.com/TokamakUI/Tokamak", from: "0.11.0"), 45 | .package(url: "https://github.com/swiftwasm/carton", from: "1.0.0"), 46 | ], 47 | targets: [ 48 | .executableTarget( 49 | name: "MyApp", 50 | dependencies: [ 51 | .product(name: "TokamakShim", package: "Tokamak") 52 | ]), 53 | ] 54 | ) 55 | ``` 56 | 57 | 4. Add your first view to `Sources/MyApp/main.swift`: 58 | 59 | ```swift 60 | import TokamakDOM 61 | 62 | @main 63 | struct TokamakApp: App { 64 | var body: some Scene { 65 | WindowGroup("Tokamak App") { 66 | ContentView() 67 | } 68 | } 69 | } 70 | 71 | struct ContentView: View { 72 | var body: some View { 73 | Text("Hello, world!") 74 | } 75 | } 76 | ``` 77 | 78 | 5. Build the project and start the development server, `swift run carton dev` can be kept running 79 | during development: 80 | 81 | ``` 82 | swift run carton dev 83 | ``` 84 | 85 | 6. Open [http://127.0.0.1:8080/](http://127.0.0.1:8080/) in your browser to see the app 86 | running. You can edit the app source code in your favorite editor and save it, `carton` 87 | will immediately rebuild the app and reload all browser tabs that have the app open. 88 | 89 | You can also clone [the Tokamak repository](https://github.com/TokamakUI/Tokamak) and run `carton 90 | dev --product TokamakDemo` in its root directory. This will build the demo app that shows almost all of the currently 91 | implemented APIs. 92 | -------------------------------------------------------------------------------- /src/getting-started/chrome-devtools-swift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/swiftwasm-book/d6dbb4fd28dd0d1316f891b18f88db45186a5319/src/getting-started/chrome-devtools-swift.png -------------------------------------------------------------------------------- /src/getting-started/chrome-devtools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/swiftwasm-book/d6dbb4fd28dd0d1316f891b18f88db45186a5319/src/getting-started/chrome-devtools.png -------------------------------------------------------------------------------- /src/getting-started/concurrency.md: -------------------------------------------------------------------------------- 1 | # Running `async` functions in WebAssembly 2 | 3 | On macOS, iOS, and Linux, `libdispatch`-based executor is used by default, but `libdispatch` is not supported in single-threaded WebAssembly environment. 4 | However, there are still two global task executors available in SwiftWasm. 5 | 6 | If you need multi-threading support on Web, please see [Multithreading guide](./multithreading.md) for more details. 7 | 8 | ## Cooperative Task Executor 9 | 10 | `Cooperative Task Executor` is the default task executor in SwiftWasm. It is a simple single-threaded cooperative task executor implemented in [Swift Concurrency library](https://github.com/apple/swift/blob/0c67ce64874d83b2d4f8d73b899ee58f2a75527f/stdlib/public/Concurrency/CooperativeGlobalExecutor.inc). 11 | If you are not familiar with "Cooperative" in concurrent programming term, see [its definition for more details](https://en.wikipedia.org/wiki/Cooperative_multitasking). 12 | 13 | This executor has an *event loop* that dispatches tasks until no more tasks are enqueued, and exits immediately after all tasks are dispatched. 14 | Note that this executor won't yield control to the host environment during execution, so any host's async operation cannot call back to the Wasm execution. 15 | 16 | This executor is suitable for WASI command line tools, or host-independent standalone applications. 17 | 18 | ```swift 19 | // USAGE 20 | // $ swiftc -target wasm32-unknown-wasi -parse-as-library main.swift -o main.wasm 21 | // $ wasmtime main.wasm 22 | @main 23 | struct Main { 24 | static func main() async throws { 25 | print("Sleeping for 1 second... 😴") 26 | try await Task.sleep(nanoseconds: 1_000_000_000) 27 | print("Wake up! 😁") 28 | } 29 | } 30 | ``` 31 | 32 | ## JavaScript Event Loop-based Task Executor 33 | 34 | `JavaScript Event Loop-based Task Executor` is a task executor that cooperates with the JavaScript's event loop. It is provided by [`JavaScriptKit`](https://github.com/swiftwasm/JavaScriptKit), and you need to activate it explicitly by calling a predefined `JavaScriptEventLoop.installGlobalExecutor()` function (see below for more details). 35 | 36 | This executor also has its own *event loop* that dispatches tasks until no more tasks are enqueued synchronously. 37 | It yields control to the JavaScript side after all pending tasks are dispatched, so JavaScript program can call back to the executed Wasm module. 38 | After a task is resumed by callbacks from JavaScript, the executor starts its event loop again in the next microtask tick. 39 | 40 | To enable this executor, you need to use `JavaScriptEventLoop` module, which is provided as a part of `JavaScriptKit` package. 41 | 42 | 0. Ensure that you added `JavaScriptKit` dependency to your `Package.swift` 43 | 1. Add `JavaScriptEventLoop` dependency to your targets that use this executor 44 | 45 | ```swift 46 | .product(name: "JavaScriptEventLoop", package: "JavaScriptKit"), 47 | ``` 48 | 2. Import `JavaScriptEventLoop` and call `JavaScriptEventLoop.installGlobalExecutor()` before spawning any tasks to activate the executor instead of the default cooperative executor. 49 | 50 | Note that this executor is only available on JavaScript host environment. 51 | 52 | See also [`JavaScriptKit` package `README`](https://github.com/swiftwasm/JavaScriptKit/#asyncawait) for more details. 53 | 54 | ```swift 55 | import JavaScriptEventLoop 56 | import JavaScriptKit 57 | 58 | JavaScriptEventLoop.installGlobalExecutor() 59 | 60 | let document = JSObject.global.document 61 | var asyncButtonElement = document.createElement("button") 62 | _ = document.body.appendChild(asyncButtonElement) 63 | 64 | asyncButtonElement.innerText = "Fetch Zen" 65 | func printZen() async throws { 66 | let fetch = JSObject.global.fetch.function! 67 | let response = try await JSPromise(fetch("https://api.github.com/zen").object!)!.value 68 | let text = try await JSPromise(response.text().object!)!.value 69 | print(text) 70 | } 71 | asyncButtonElement.onclick = .object(JSClosure { _ in 72 | Task { 73 | do { 74 | try await printZen() 75 | } catch { 76 | print(error) 77 | } 78 | } 79 | 80 | return .undefined 81 | }) 82 | ``` 83 | -------------------------------------------------------------------------------- /src/getting-started/coverage-support.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/swiftwasm-book/d6dbb4fd28dd0d1316f891b18f88db45186a5319/src/getting-started/coverage-support.png -------------------------------------------------------------------------------- /src/getting-started/debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | Debugging is one of the most important parts of application development. This section describes debugging tools compatible with SwiftWasm. 4 | 5 | These tools are DWARF-based, so you need to build your application with DWARF sections enabled. 6 | If you are using `carton bundle`, you can use the `--debug-info` flag to enable debugging with optimized application. 7 | If you are using `swift build`, it is enabled by default. 8 | 9 | ## Chrome DevTools 10 | 11 | When you are debugging a web browser application, Chrome DevTools is a good tool to use. It allows you to 12 | put breakpoints and step through at Swift source code level. 13 | 14 | ### Official DWARF Extension 15 | 16 | Install [`C/C++ DevTools Support (DWARF)`](https://goo.gle/wasm-debugging-extension) extension in your Chrome 17 | 18 | See [the DevTools team's official introduction](https://developer.chrome.com/blog/wasm-debugging-2020) for more details about the extension. 19 | 20 | ![](./chrome-devtools.png) 21 | 22 | Note that the function names in the stack trace are mangled. You can demangle them using `swift demangle` command. 23 | 24 | Unfortunately, variable inspection is unavailable since Swift depends on its own mechanisms to do that instead of DWARF's structure type feature. If you need this feature, you can use the enhanced extension below. 25 | 26 | ### Enhanced DWARF Extension for Swift 27 | 28 | For a better Swift debugging experience, there's also an enhanced version of the DWARF extension specifically for Swift. This extension enables: 29 | 30 | - Breakpoint setting and Swift code inspection 31 | - Human-readable call stack frames 32 | - Swift variable value inspection 33 | 34 | To install this enhanced extension: 35 | 36 | 1. First, uninstall the official "C/C++ DevTools Support (DWARF)" extension if you have it installed 37 | 2. Download the extension ZIP file from [GitHub Releases](https://github.com/GoodNotes/devtools-frontend/releases/tag/swift-0.2.3.0) 38 | 3. Go to `chrome://extensions/` and enable "Developer mode" 39 | 4. Drag and drop the downloaded ZIP file into the page 40 | 41 | When you close and reopen the DevTools window, DevTools will suggest reloading itself to apply settings. 42 | 43 | Note: There is a known issue where some JavaScriptKit types like `JSObject` and `JSValue` are not shown in pretty format in the variables view. 44 | 45 | ![](./chrome-devtools-swift.png) 46 | 47 | ## [wasminspect](https://github.com/kateinoigakukun/wasminspect) 48 | 49 | [wasminspect](https://github.com/kateinoigakukun/wasminspect) 50 | can help in the investigation if the debugged binary does not rely on integration with JavaScript. 51 | We recommend splitting your packages into separate executable targets, most of which shouldn't 52 | assume the availability of JavaScript to make debugging easier. 53 | 54 | ![demo](https://raw.githubusercontent.com/kateinoigakukun/wasminspect/master/assets/demo.gif) 55 | 56 | ## [wasm-memprof](https://github.com/kateinoigakukun/wasm-memprof) 57 | 58 | If you are debugging memory leaks, [wasm-memprof](https://github.com/kateinoigakukun/wasm-memprof) can help you. 59 | It is a tool to profile memory usage of WebAssembly applications with a few lines of setup code: 60 | 61 | ```javascript 62 | import { WMProf } from "wasm-memprof"; 63 | import { SwiftDemangler } from "wasm-memprof/plugins/swift-demangler.js"; 64 | 65 | const swiftDemangler = SwiftDemangler.create(); 66 | const WebAssembly = WMProf.wrap(globalThis.WebAssembly, { 67 | demangler: swiftDemangler.demangle.bind(swiftDemangler), 68 | }); 69 | ``` 70 | 71 | Check the repository for more details. 72 | 73 | swift-wmprof 74 | 75 | -------------------------------------------------------------------------------- /src/getting-started/hello-world.md: -------------------------------------------------------------------------------- 1 | # Hello, World! 2 | 3 | > This page has been merged into the [Installation](./setup.md) page. 4 | -------------------------------------------------------------------------------- /src/getting-started/index.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | This is a getting started guide section to use SwiftWasm. 4 | 5 | You can learn about: 6 | 7 | - How to set up a Swift toolchain for compiling to WebAssembly 8 | - How to compile a simple Swift code and Swift Package into WebAssembly 9 | - How to interoperate with JavaScript -------------------------------------------------------------------------------- /src/getting-started/javascript-interop.md: -------------------------------------------------------------------------------- 1 | # JavaScript interoperation 2 | 3 | [JavaScriptKit](https://github.com/swiftwasm/JavaScriptKit) is a Swift framework to interact with JavaScript through WebAssembly. 4 | 5 | You can use any JavaScript API from Swift with this library. Here's a quick example of JavaScriptKit 6 | usage in a browser app: 7 | 8 | ```swift 9 | import JavaScriptKit 10 | 11 | let document = JSObject.global.document 12 | 13 | var divElement = document.createElement("div") 14 | divElement.innerText = "Hello, world" 15 | _ = document.body.appendChild(divElement) 16 | ``` 17 | 18 | You can also use JavaScriptKit in SwiftWasm apps integrated with Node.js, as there no assumptions 19 | that any browser API is present in the library. 20 | 21 | JavaScriptKit consists of a runtime library package [hosted on 22 | npm](https://www.npmjs.com/package/javascript-kit-swift), and a SwiftPM package for the API on the 23 | Swift side. To integrate the JavaScript runtime automatically into your app, we recommend following 24 | the corresponding [guide for browser apps in our book](./browser-app.md). 25 | 26 | 27 | You can get more detailed JavaScriptKit documentation [here](https://swiftwasm.github.io/JavaScriptKit/). 28 | -------------------------------------------------------------------------------- /src/getting-started/multithreading.md: -------------------------------------------------------------------------------- 1 | # Using threads in WebAssembly 2 | 3 | ## Background 4 | 5 | While the WebAssembly spec defines [atomic operations](https://github.com/WebAssembly/threads), 6 | it does not define a way to create threads. This means that WebAssembly modules can't create 7 | threads themselves, and the host environment must provide a way to create threads and run 8 | WebAssembly modules on them. 9 | 10 | ## [`wasi-threads`](https://github.com/WebAssembly/wasi-threads) proposal 11 | 12 | The WebAssembly System Interface (WASI) had a proposal for adding thread creation APIs to WASI. 13 | The proposal was implemented in several WASI host runtimes, including Wasmtime and wasm-micro-runtime, 14 | but it was [withdrawn in August 2023](https://github.com/WebAssembly/wasi-threads/issues/48#issuecomment-1696407630) in favor of [shared-everything-threads](https://github.com/WebAssembly/shared-everything-threads) proposal. However, the shared-everything-threads proposal is still in the early stages of development and is not yet available in any WASI host runtime. So, for now, we are employing the `wasi-threads` ABI in SwiftWasm to provide thread support immediately. 15 | 16 | The `wasi-threads` feature is only available in the `wasm32-unknown-wasip1-threads` target triple, which is explicitly distinct from the `wasm32-unknown-wasi` target triple. 17 | 18 | The `wasm32-unknown-wasip1-threads` target triple is only available in the nightly Swift SDK for WebAssembly. 19 | 20 | You can run WebAssembly programs built with the `wasm32-unknown-wasip1-threads` target by using the `wasmtime` runtime with the `--wasi threads` flag. 21 | Check [a recent nightly Swift SDK release](https://github.com/swiftwasm/swift/releases) and how to install it [here](./setup-snapshot.md). 22 | 23 | ```console 24 | # Assume you are using swift-DEVELOPMENT-SNAPSHOT-2024-12-04-a toolchain 25 | $ swift sdk install https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-12-05-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-12-05-a-wasm32-unknown-wasip1-threads.artifactbundle.zip --checksum 1796ae86f3c90b45d06ee29bb124577aa4135585bbd922430b6d1786f855697d 26 | $ swift build --swift-sdk wasm32-unknown-wasip1-threads 27 | # Enable the `wasi-threads` feature in wasmtime 28 | $ wasmtime --wasi threads .build/debug/Example.wasm 29 | ``` 30 | 31 | Note that even with the `wasi-threads` feature, the default concurrency execution model is still single-threaded as we have not yet ported libdispatch. The `wasi-threads` feature is only used to provide a low-level `pthread` API for creating threads. 32 | 33 | ## `WebWorkerTaskExecutor` - shared-everything concurrency 34 | 35 | We provide `WebWorkerTaskExecutor`, a [`TaskExecutor`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0417-task-executor-preference.md) implementation that runs `Task`s in a Web Worker. This allows you to run Swift code concurrently in a Web Worker sharing the same memory space. 36 | 37 | This feature is available in the `JavaScriptKit` package and you need to use `wasm32-unkonwn-wasip1-threads` target and [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) to use it. 38 | 39 | See more details in the following links: 40 | 41 | - [Add `WebWorkerTaskExecutor` · Pull Request #256 · swiftwasm/JavaScriptKit](https://github.com/swiftwasm/JavaScriptKit/pull/256) 42 | - [JavaScriptKit/Examples/Multithreading at main · swiftwasm/JavaScriptKit](https://github.com/swiftwasm/JavaScriptKit/tree/main/Examples/Multithreading) 43 | - [WebWorkerTaskExecutor | Documentation](https://swiftpackageindex.com/swiftwasm/javascriptkit/main/documentation/javascripteventloop/webworkertaskexecutor) 44 | 45 | ## `WebWorkerKit` - shared-nothing concurrency 46 | 47 | If you can't use `SharedArrayBuffer` or want to run Swift code in a separate memory space, you can use `WebWorkerKit`. `WebWorkerKit` is a library that provides a way to run Swift Distributed Actors in their own worker "thread" in a Web Worker. It's message-passing based and allows you to run Swift code concurrently in a Web Worker without sharing memory space. 48 | 49 | Check the repository for more details: [swiftwasm/WebWorkerKit: A way of running Swift Distributed Actors in their own worker "thread"](https://github.com/swiftwasm/WebWorkerKit) 50 | -------------------------------------------------------------------------------- /src/getting-started/porting.md: -------------------------------------------------------------------------------- 1 | # Porting code to WebAssembly with WASI 2 | 3 | In the form that's currently standardized and supported by browsers and other hosts, WebAssembly 4 | is a 32-bit architecture. You have to take this into account when porting code from other 5 | platforms, since `Int` type is a signed 32-bit integer, and `UInt` is an unsigned 32-bit integer 6 | when building with SwiftWasm. You'll need to audit codepaths that cast 64-bit integers to `Int` 7 | or `UInt`, and a good amount of cross-platform test coverage can help with that. 8 | 9 | Additionally, there a differences in APIs exposed by the standard C library and Swift core 10 | libraries which we discuss in the next few subsections. 11 | 12 | ## `WASILibc` module 13 | 14 | When porting existing projects from other platforms to SwiftWasm you might stumble upon code that 15 | relies on importing a platform-specific [C 16 | library](https://en.wikipedia.org/wiki/C_standard_library) module directly. It looks like `import 17 | Glibc` on Linux, or `import Darwin` on Apple platforms. Fortunately, most of the standard C library 18 | APIs are available when using SwiftWasm, you just need to use `import WASILibc` to get access to it. 19 | Most probably you want to preserve compatibility with other platforms, thus your imports would look 20 | like this: 21 | 22 | ```swift 23 | #if canImport(Darwin) 24 | import Darwin 25 | #elseif canImport(Glibc) 26 | import Glibc 27 | #elseif canImport(WASILibc) 28 | import WASILibc 29 | #endif 30 | ``` 31 | 32 | ### Limitations 33 | 34 | WebAssembly and [WASI](https://wasi.dev/) provide a constrained environment, which currently does 35 | not directly support multi-threading. Thus, you should not 36 | expect these APIs to work when importing `WASILibc`. Please be aware of these limitations when 37 | porting your code, which also has an impact on what [can be supported in the Swift 38 | Foundation](#swift-foundation-and-dispatch) at the moment. 39 | 40 | ## Swift Foundation and Dispatch 41 | 42 | [The Foundation core library](https://swift.org/core-libraries/#foundation) is available in 43 | SwiftWasm, but in a limited capacity. The main reason is that [the Dispatch core 44 | library](https://swift.org/core-libraries/#libdispatch) is unavailable due to [the lack of 45 | standardized multi-threading support](https://github.com/swiftwasm/swift/issues/1887) in WebAssembly 46 | and SwiftWasm itself. Many Foundation APIs rely on the presence of Dispatch under the hood, 47 | specifically threading helpers. A few other types are unavailable in browsers 48 | or aren't standardized in WASI hosts, such as support for sockets and low-level networking, 49 | and they had to be disabled. These types are therefore absent in SwiftWasm Foundation: 50 | 51 | | Type or module | Status | 52 | |----------------|--------| 53 | | `FoundationNetworking` | ❌ Unavailable | 54 | | `FileManager` | ✅ Available after 6.0 | 55 | | `Host` | ✅ Partially available after 6.0 | 56 | | `Notification` | ✅ Available after 6.0 | 57 | | `NotificationQueue` | ❌ Unavailable | 58 | | `NSKeyedArchiver` | ✅ Available after 6.0 | 59 | | `NSKeyedArchiverHelpers` | ✅ Available after 6.0 | 60 | | `NSKeyedCoderOldStyleArray` | ✅ Available after 6.0 | 61 | | `NSKeyedUnarchiver` | ✅ Available after 6.0 | 62 | | `NSNotification` | ✅ Available after 6.0 | 63 | | `NSSpecialValue` | ✅ Available after 6.0 | 64 | | `Port` | ✅ Available after 6.0 | 65 | | `PortMessage` | ✅ Available after 6.0 | 66 | | `Process` | ❌ Unavailable | 67 | | `ProcessInfo` | ✅ Partially available after 5.7 | 68 | | `PropertyListEncoder` | ✅ Available after 6.0 | 69 | | `RunLoop` | ❌ Unavailable | 70 | | `Stream` | ✅ Partially available after 6.0 | 71 | | `SocketPort` | ❌ Unavailable | 72 | | `Thread` | ❌ Unavailable | 73 | | `Timer` | ❌ Unavailable | 74 | | `UserDefaults` | ✅ Available after 6.0 | 75 | 76 | Related functions and properties on other types are also absent or disabled. We would like to make 77 | them available in the future as soon as possible, and [we invite you to 78 | contribute](../contribution-guide/index.md) and help us in achieving this goal! 79 | 80 | 81 | ## XCTest 82 | 83 | [The swift-corelibs-xctest project](https://github.com/swiftlang/swift-corelibs-xctest) is available 84 | in WebAssembly platforms, and you can use it to write tests for your SwiftWasm projects. 85 | 86 | The following XCTest features are unavailable in SwiftWasm: 87 | 88 | | API | Status | 89 | |----------------|--------| 90 | | `XCTestExpectation` | ❌ Unavailable | 91 | | `XCTNSPredicateExpectation` | ❌ Unavailable | 92 | | `XCTNSNotificationExpectation` | ❌ Unavailable | 93 | | `XCTWaiter` | ❌ Unavailable | 94 | | `XCTest.perform` | ❌ Unavailable | 95 | | `XCTest.run` | ❌ Unavailable | 96 | -------------------------------------------------------------------------------- /src/getting-started/setup-snapshot.md: -------------------------------------------------------------------------------- 1 | # Installation - Development Snapshot 2 | 3 | > This page has been merged into the [Installation](./setup.md) page. 4 | -------------------------------------------------------------------------------- /src/getting-started/setup.md: -------------------------------------------------------------------------------- 1 | # Installation - Latest Release (Swift 6.1) 2 | 3 | SwiftWasm provides [Swift SDK](https://github.com/apple/swift-evolution/blob/main/proposals/0387-cross-compilation-destinations.md)s for WebAssembly. 4 | 5 | Before installing the Swift SDK, you need to ensure the following: 6 | 7 | - You need to [install an Open Source toolchain from swift.org](https://www.swift.org/install/). (Not the Xcode toolchain) 8 | - You cannot use toolchains bundled with Xcode to use the Swift SDK. 9 | - If you are using macOS, please follow the [official guide](https://www.swift.org/install/macos/package_installer/) to install the toolchain. 10 | 11 | Please ensure you have installed the Swift 6.1 Open Source toolchain. 12 | 13 | ```sh 14 | swift --version 15 | ``` 16 | 17 | | Toolchain | Output | 18 | |-----------|--------| 19 | | ❌ Xcode | `Apple Swift version 6.1 (swiftlang-6.1.0.110.21 clang-1700.0.13.3)` | 20 | | ✅ Open Source (macOS) | `Apple Swift version 6.1 (swift-6.1-RELEASE)` | 21 | | ✅ Open Source (Others) | `Swift version 6.1 (swift-6.1-RELEASE)` | 22 | 23 | Once you have installed the Open Source toolchain, you can install the Swift SDK for WebAssembly: 24 | 25 | ```bash 26 | swift sdk install "https://github.com/swiftwasm/swift/releases/download/swift-wasm-6.1-RELEASE/swift-wasm-6.1-RELEASE-wasm32-unknown-wasi.artifactbundle.zip" --checksum "7550b4c77a55f4b637c376f5d192f297fe185607003a6212ad608276928db992" 27 | ``` 28 | 29 | After installing the Swift SDK, you can see `6.1-RELEASE-wasm32-unknown-wasi` in the Swift SDK list: 30 | 31 | ```bash 32 | swift sdk list 33 | ``` 34 | 35 | You can also find other SDKs from [the GitHub Releases page](https://github.com/swiftwasm/swift/releases). 36 | 37 | ## Hello, World 38 | 39 | First, create a new directory for your project and navigate into it: 40 | 41 | ```bash 42 | $ mkdir hello && cd hello 43 | ``` 44 | 45 | Create a new Swift package: 46 | 47 | ```bash 48 | $ swift package init --type executable 49 | ``` 50 | 51 | You can use the installed SDKs to cross-compile Swift packages for WebAssembly: 52 | 53 | ```bash 54 | $ swift build --swift-sdk wasm32-unknown-wasi 55 | ... 56 | $ file .build/wasm32-unknown-wasi/debug/hello.wasm 57 | .build/wasm32-unknown-wasi/debug/hello.wasm: WebAssembly (wasm) binary module version 0x1 (MVP) 58 | ``` 59 | 60 | You can run the built WebAssembly module using [`wasmtime`](https://wasmtime.dev/): 61 | 62 | ```bash 63 | $ wasmtime .build/wasm32-unknown-wasi/debug/hello.wasm 64 | Hello, world! 65 | ``` 66 | 67 | ## FAQ 68 | 69 | ### How to check if I am using Open Source toolchain or Xcode toolchain? 70 | 71 | ```bash 72 | $ swift --version | head -n1 73 | ``` 74 | 75 | | Toolchain | Output | 76 | |-----------|--------| 77 | | Xcode | `Apple Swift version 6.1 (swiftlang-6.1.0.110.21 clang-1700.0.13.3)` | 78 | | Open Source (macOS) | `Apple Swift version 6.1 (swift-6.1-RELEASE)` | 79 | | Open Source (Others) | `Swift version 6.1 (swift-6.1-RELEASE)` | 80 | 81 | 82 | ### `:0: error: module compiled with Swift 6.1 cannot be imported by the Swift x.y.z` 83 | 84 | This error occurs when the Swift toolchain version you are using is different from the version of the Swift SDK you have installed. 85 | 86 | To resolve this issue, you can either: 87 | 88 | 1. Switch the Swift toolchain to the same version as the Swift SDK you have installed. Check the [official guide](https://www.swift.org/install/macos/package_installer/) for how to switch the toolchain. 89 | 2. Install the Swift SDK for the same version as the Swift toolchain you are using. 90 | Use the following shell snippet to query compatible Swift SDK for your current toolchain version: 91 | 92 | ```console 93 | ( 94 | V="$(swiftc --version | head -n1)"; \ 95 | TAG="$(curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/tag-by-version.json" | jq -r --arg v "$V" '.[$v] | .[-1]')"; \ 96 | curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/builds/$TAG.json" | \ 97 | jq -r '.["swift-sdks"]["wasm32-unknown-wasi"] | "swift sdk install \"\(.url)\" --checksum \"\(.checksum)\""' 98 | ) 99 | ``` 100 | 101 | 102 | ### What is the difference between the Swift Toolchain and the Swift SDK? 103 | 104 | The Swift toolchain is a complete package that includes the Swift compiler, standard library, and other tools. 105 | 106 | The Swift SDK includes a subset of the Swift toolchain that includes only the necessary components for cross-compilation and some supplementary resources. 107 | 108 | ### What is included in the Swift SDK for WebAssembly? 109 | 110 | The Swift SDK for WebAssembly includes only the pre-built Swift standard libraries for WebAssembly. It does not include the Swift compiler or other tools that are part of the Swift toolchain. 111 | -------------------------------------------------------------------------------- /src/getting-started/swift-package.md: -------------------------------------------------------------------------------- 1 | # Compile a SwiftPM package to WebAssembly 2 | 3 | > This page has been merged into the [Installation](./setup.md) page. 4 | -------------------------------------------------------------------------------- /src/getting-started/testing.md: -------------------------------------------------------------------------------- 1 | # Testing your app 2 | 3 | You can write a test suite for your SwiftWasm app or library, or run an existing test suite 4 | written for `XCTest` if you port existing code to SwiftWasm. Your project has to have a 5 | `Package.swift` package manifest for this to work. We assume that you use SwiftPM to build your 6 | project and that you have a working package manifest. Please follow [our SwiftPM guide](./swift-package.md) for new projects. 7 | 8 | ## A simple test case 9 | 10 | Let's assume you have an `Example` target in your project that you'd like to test. Your 11 | `Package.swift` should also have a test suite target with a dependency on the library target. It 12 | would probably look like this: 13 | 14 | ```swift 15 | // swift-tools-version: 5.9 16 | 17 | import PackageDescription 18 | 19 | let package = Package( 20 | name: "Example", 21 | products: [ 22 | .library(name: "Example", targets: ["Example"]), 23 | ], 24 | targets: [ 25 | .target(name: "Example"), 26 | .testTarget(name: "ExampleTests", dependencies: ["Example"]), 27 | ] 28 | ) 29 | ``` 30 | 31 | Now you should make sure there's `Tests/ExampleTests` subdirectory in your project. 32 | If you don't have any files in it yet, create `ExampleTests.swift` in it: 33 | 34 | ```swift 35 | import Example 36 | import XCTest 37 | 38 | final class ExampleTests: XCTestCase { 39 | func testTrivial() { 40 | XCTAssertEqual(text, "Hello, world") 41 | } 42 | } 43 | ``` 44 | 45 | This code assumes that your `Example` defines some `text` with `"Hello, world"` value 46 | for this test to pass. Your test functions should all start with `test`, please see [XCTest 47 | documentation](https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods) 48 | for more details. 49 | 50 | ## Building and running the test suite with `SwiftPM` 51 | 52 | You can build your test suite by running this command in your terminal: 53 | 54 | ```sh 55 | $ swift build --build-tests --triple wasm32-unknown-wasi 56 | ``` 57 | 58 | If you're used to running `swift test` to run test suites for other Swift platforms, we have to 59 | warn you that this won't work. `swift test` doesn't know what WebAssembly environment you'd like to 60 | use to run your tests. Because of this building tests and running them are two separate steps when 61 | using `SwiftPM`. After your tests are built, you can use a WASI-compatible host such as 62 | [wasmtime](https://wasmtime.dev/) to run the test bundle: 63 | 64 | ```sh 65 | $ wasmtime --dir . .build/wasm32-unknown-wasi/debug/ExamplePackageTests.wasm 66 | ``` 67 | 68 | (`--dir .` is used to allow XCTest to find `Bundle.main` resources placed alongside the executable file.) 69 | 70 | As you can see, the produced test binary starts with the name of your package followed by 71 | `PackageTests.wasm`. It is located in the `.build/debug` subdirectory, or in the `.build/release` 72 | subdirectory when you build in release mode. 73 | 74 | ## Code coverage with `SwiftPM` 75 | 76 | > **Note**: Code coverage support is available only in 6.1 and later. 77 | 78 | You can also generate code coverage reports for your test suite. To do this, you need to build your 79 | test suite with the `--enable-code-coverage` and linker options `-Xlinker -lwasi-emulated-getpid`: 80 | 81 | ```sh 82 | $ swift build --build-tests --swift-sdk wasm32-unknown-wasi --enable-code-coverage -Xlinker -lwasi-emulated-getpid 83 | ``` 84 | 85 | After building your test suite, you can run it with `wasmtime` as described above. The raw coverage 86 | data will be stored in `default.profraw` file in the current directory. You can use the `llvm-profdata` 87 | and `llvm-cov` tools to generate a human-readable report: 88 | 89 | ```sh 90 | $ wasmtime --dir . .build/wasm32-unknown-wasi/debug/ExamplePackageTests.wasm 91 | $ llvm-profdata merge default.profraw -o default.profdata 92 | $ llvm-cov show .build/wasm32-unknown-wasi/debug/ExamplePackageTests.wasm -instr-profile=default.profdata 93 | # or generate an HTML report 94 | $ llvm-cov show .build/wasm32-unknown-wasi/debug/ExamplePackageTests.wasm -instr-profile=default.profdata --format=html -o coverage 95 | $ open coverage/index.html 96 | ``` 97 | 98 | ![](./coverage-support.png) 99 | 100 | ## Building and running the test suite with `carton` 101 | 102 | If you use [`carton`](https://carton.dev) to develop and build your app, as described in [our guide 103 | for browser apps](./browser-app.md), just run `swift run carton test` in the 104 | root directory of your package. This will automatically build the test suite and run it with a WASI runtime for you. 105 | -------------------------------------------------------------------------------- /src/getting-started/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | These are some common issues you may run into while using SwiftWasm. 4 | 5 | If you are having trouble that is not listed here, try searching for it in the [SwiftWasm issue tracker](https://github.com/swiftwasm/swift/issues). 6 | If you are still having trouble, please file an issue or contact us at [the community Discord server](https://discord.gg/ashJW8T8yp). 7 | 8 | 9 | ## `RuntimeError: memory access out of bounds` 10 | 11 | If you encounter this error, there are 3 possible causes: 12 | 13 | ### 1. You are trying to access invalid memory in your code 14 | 15 | In this case, you need to make sure which memory operations are invalid in your code by `UnsafePointer` or C code. 16 | 17 | ### 2. You missed program initialization defined in [WASI Application ABI](https://github.com/WebAssembly/WASI/blob/bac366c8aeb69cacfea6c4c04a503191bf1cede1/legacy/application-abi.md). 18 | 19 | If your application is used as a library, you need to follow [*WASI reactor ABI*](https://github.com/WebAssembly/WASI/blob/bac366c8aeb69cacfea6c4c04a503191bf1cede1/legacy/application-abi.md). 20 | 21 | Please make sure that you followed it by reviewing the [Exporting function guide](../examples/exporting-function.md) 22 | 23 | ### 3. Stack overflow is occurring. 24 | 25 | If you are using `--stack-first` linker option (carton uses it by default), you can face `RuntimeError: memory access out of bounds` error due to stack overflow. 26 | 27 | You have two options to solve this issue: 28 | 1. Avoid recursive calls if possible. 29 | 2. Extend the stack size by linker option `-z stack-size=`. [The default stack size is 64KB](https://github.com/llvm/llvm-project/blob/fabe915705472e2c06ed1aa9a90620462594e82f/llvm/include/llvm/BinaryFormat/Wasm.h#L32) 30 | ``` 31 | swift build --triple wasm32-unknown-wasi -Xlinker -z -Xlinker stack-size=131072 32 | ``` 33 | 34 | 2. Identify which function consumes a lof of stack space by some tools like [wasm-stack-consumer](https://github.com/kateinoigakukun/wasm-stack-consumer) 35 | 36 | 37 | See also: [LLVM Bugzilla – wasm32: Allow placing the stack before global data](https://bugs.llvm.org/show_bug.cgi?id=37181) 38 | 39 | ## `fatal error: 'stdlib.h' file not found` 40 | 41 | If you encounter this error, please make sure that: 42 | 43 | - You are using SwiftWasm toolchain (if you installed it as a toolchain, not as a Swift SDK) 44 | - Check `which swift` and make sure it points to the SwiftWasm toolchain. 45 | - You are using the correct target triple: 46 | - `--triple wasm32-unknown-wasi --static-swift-stdlib` if you installed as a *toolchain* 47 | - `--swift-sdk wasm32-unknown-wasi` if you installed as a *Swift SDK* 48 | 49 | ## `error: missing external dependency '.../usr/lib/swift/wasi/static-executable-args.lnk'` 50 | 51 | You may encounter this error while building with Swift SDK for WebAssembly and `swiftc` driver command. Unfortunately, Swift SDK does not support building with `swiftc` command yet, so you need to use `swift build` Swift Package Manager command instead. 52 | 53 | e.g. `swift build --swift-sdk ` 54 | 55 | See also: [Compile a SwiftPM package to WebAssembly](./swift-package.md) 56 | -------------------------------------------------------------------------------- /src/getting-started/vscode-editing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/swiftwasm-book/d6dbb4fd28dd0d1316f891b18f88db45186a5319/src/getting-started/vscode-editing.png -------------------------------------------------------------------------------- /src/getting-started/vscode.md: -------------------------------------------------------------------------------- 1 | # Configuring Visual Studio Code with WebAssembly SDK 2 | 3 | This guide will help you configure Visual Studio Code (VSCode) to use the Swift SDK for WebAssembly. 4 | 5 | > **Note**: This guide assumes you have already installed the [Swift SDK for WebAssembly](./setup-snapshot.md) from the development snapshot release. 6 | 7 | ## Prerequisites 8 | 9 | - [Visual Studio Code](https://code.visualstudio.com/) 10 | - [Swift for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang) 11 | - [Swift OSS Toolchain](https://swift.org/install) (6.1 or later) 12 | - [Swift SDK for WebAssembly](./setup-snapshot.md) 13 | 14 | 15 | ## Configure your SwiftPM package 16 | 17 | 1. Open your Swift package in VSCode. 18 | 2. Create a `.vscode/settings.json` with the following content: 19 | 20 | ```json 21 | { 22 | "swift.path": "/usr/bin", 23 | } 24 | ``` 25 | 26 | > **Note**: Replace `` with the path to the development snapshot Swift toolchain you downloaded from [swift.org/install](https://www.swift.org/install). 27 | 28 | 3. Create a `.sourcekit-lsp/config.json` with the following content: 29 | 30 | ```json 31 | { 32 | "swiftPM": { 33 | "swiftSDK": "" 34 | } 35 | } 36 | ``` 37 | 38 | > **Note**: Replace `` with the Swift SDK id you installed using the `swift sdk install` command. You can find the installed SDK id by running `swift sdk list`. 39 | 40 | 4. Reload the VSCode window by pressing `Cmd + Shift + P` and selecting `Reload Window`. 41 | 42 | That's it! You can now build and auto-complete your Swift package using the Swift SDK for WebAssembly. 43 | 44 | ![](./vscode-editing.png) 45 | --------------------------------------------------------------------------------