• Stars
    star
    583
  • Rank 76,663 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 4 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Swift framework to interact with JavaScript through WebAssembly.

JavaScriptKit

Run unit tests

Swift framework to interact with JavaScript through WebAssembly.

Getting started

This JavaScript code

const alert = window.alert;
const document = window.document;

const divElement = document.createElement("div");
divElement.innerText = "Hello, world";
const body = document.body;
body.appendChild(divElement);

const pet = {
  age: 3,
  owner: {
    name: "Mike",
  },
};

alert("JavaScript is running on browser!");

Can be written in Swift using JavaScriptKit

import JavaScriptKit

let document = JSObject.global.document

var divElement = document.createElement("div")
divElement.innerText = "Hello, world"
_ = document.body.appendChild(divElement)

struct Owner: Codable {
  let name: String
}

struct Pet: Codable {
  let age: Int
  let owner: Owner
}

let jsPet = JSObject.global.pet
let swiftPet: Pet = try JSValueDecoder().decode(from: jsPet)

_ = JSObject.global.alert!("Swift is running in the browser!")

async/await

Starting with SwiftWasm 5.5 you can use async/await with JSPromise objects. This requires a few additional steps though (you can skip these steps if your app depends on Tokamak):

  1. Make sure that your target depends on JavaScriptEventLoop in your Packages.swift:
.target(
    name: "JavaScriptKitExample",
    dependencies: [
        "JavaScriptKit",
        .product(name: "JavaScriptEventLoop", package: "JavaScriptKit")
    ]
)
  1. Add an explicit import in the code that executes *before you start using await and/or Task APIs (most likely in main.swift):
import JavaScriptEventLoop
  1. Run this function *before you start using await and/or Task APIs (again, most likely in main.swift):
JavaScriptEventLoop.installGlobalExecutor()

Then you can await on the value property of JSPromise instances, like in the example below:

import JavaScriptKit
import JavaScriptEventLoop

let alert = JSObject.global.alert.function!
let document = JSObject.global.document

private let jsFetch = JSObject.global.fetch.function!
func fetch(_ url: String) -> JSPromise {
    JSPromise(jsFetch(url).object!)!
}

JavaScriptEventLoop.installGlobalExecutor()

struct Response: Decodable {
    let uuid: String
}

var asyncButtonElement = document.createElement("button")
asyncButtonElement.innerText = "Fetch UUID demo"
asyncButtonElement.onclick = .object(JSClosure { _ in
    Task {
        do {
            let response = try await fetch("https://httpbin.org/uuid").value
            let json = try await JSPromise(response.json().object!)!.value
            let parsedResponse = try JSValueDecoder().decode(Response.self, from: json)
            alert(parsedResponse.uuid)
        } catch {
            print(error)
        }
    }

    return .undefined
})

_ = document.body.appendChild(asyncButtonElement)

JavaScriptEventLoop activation in XCTest suites

If you need to execute Swift async functions that can be resumed by JS event loop in your XCTest suites, please add JavaScriptEventLoopTestSupport to your test target dependencies.

 .testTarget(
   name: "MyAppTests",
   dependencies: [
     "MyApp",
+    "JavaScriptEventLoopTestSupport",
   ]
 )

Linking this module automatically activates JS event loop based global executor by calling JavaScriptEventLoop.installGlobalExecutor()

Requirements

For developers

  • macOS 11 and Xcode 13.2 or later versions, which support Swift Concurrency back-deployment. To use earlier versions of Xcode on macOS 11 you'll have to add .unsafeFlags(["-Xfrontend", "-disable-availability-checking"]) in Package.swift manifest of your package that depends on JavaScriptKit. You can also use Xcode 13.0 and 13.1 on macOS Monterey, since this OS does not need back-deployment.
  • Swift 5.5 or later and Ubuntu 18.04 if you'd like to use Linux. Other Linux distributions are currently not supported.

For users of apps depending on JavaScriptKit

Any recent browser that supports WebAssembly and required JavaScript features should work, which currently includes:

  • Edge 84+
  • Firefox 79+
  • Chrome 84+
  • Desktop Safari 14.1+
  • Mobile Safari 14.8+

If you need to support older browser versions, you'll have to build with the JAVASCRIPTKIT_WITHOUT_WEAKREFS flag, passing -Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS flags when compiling. This should lower browser requirements to these versions:

  • Edge 16+
  • Firefox 61+
  • Chrome 66+
  • (Mobile) Safari 12+

Not all of these versions are tested on regular basis though, compatibility reports are very welcome!

Usage in a browser application

The easiest way to get started with JavaScriptKit in your browser app is with the carton bundler.

As a part of these steps you'll install carton via Homebrew on macOS (you can also use the ghcr.io/swiftwasm/carton Docker image if you prefer to run the build steps on Linux). Assuming you already have Homebrew installed, you can create a new app that uses JavaScriptKit by following these steps:

  1. Install carton:
brew install swiftwasm/tap/carton

If you had carton installed before this, make sure you have version 0.6.1 or greater:

carton --version
  1. Create a directory for your project and make it current:
mkdir SwiftWasmApp && cd SwiftWasmApp
  1. Initialize the project from a template with carton:
carton init --template basic
  1. Build the project and start the development server, carton dev can be kept running during development:
carton dev
  1. Open http://127.0.0.1:8080/ in your browser and a developer console within it. You'll see Hello, world! output in the console. You can edit the app source code in your favorite editor and save it, carton will immediately rebuild the app and reload all browser tabs that have the app open.

You can also build your project with webpack.js and a manually installed SwiftWasm toolchain. Please see the following sections and the Example directory for more information in this more advanced use case.

Manual toolchain installation

This library only supports swiftwasm/swift toolchain distribution. The toolchain can be installed via swiftenv, in the same way as the official Swift nightly toolchain.

You have to install the toolchain manually when working on the source code of JavaScriptKit itself, especially if you change anything in the JavaScript runtime parts. This is because the runtime parts are embedded in carton and currently can't be replaced dynamically with the JavaScript code you've updated locally.

Just pass a toolchain archive URL for the latest SwiftWasm 5.6 release appropriate for your platform:

$ swiftenv install "https://github.com/swiftwasm/swift/releases/download/swift-wasm-5.6.0-RELEASE/swift-wasm-5.6.0-RELEASE-macos_$(uname -m).pkg"

You can also use the install-toolchain.sh helper script that uses a hardcoded toolchain snapshot:

$ ./scripts/install-toolchain.sh
$ swift --version
Swift version 5.6 (swiftlang-5.6.0)
Target: arm64-apple-darwin20.6.0

Sponsoring

Become a gold or platinum sponsor and contact maintainers to add your logo on our README on Github with a link to your site.

More Repositories

1

carton

📦 Watcher, bundler, and test runner for your SwiftWasm apps
Swift
317
star
2

awesome-swiftwasm

A community-driven curated list of SwiftWasm projects and content
256
star
3

WasmKit

WebAssembly Runtime written in Swift
Swift
173
star
4

swiftwasm.github.io

Website for the SwiftWasm project
JavaScript
79
star
5

WebAPIKit

Access the DOM and other Web APIs from Swift! (Very much a WIP)
Swift
60
star
6

swiftwasm-pad

swiftwasm-pad is a online playground to help developers learn about Swift on Web.
Swift
54
star
7

swiftwasm-book

The SwiftWasm book to get you started
44
star
8

WasmTransformer

A Swift package for reading, writing, and transformation of WebAssembly binaries
Swift
34
star
9

wamr-swift

Swift WebAssembly runtime powered by WAMR
Swift
31
star
10

uwasi

Micro modularized WASI runtime for JavaScript
TypeScript
30
star
11

OpenCombineJS

OpenCombine helpers for JavaScriptKit/WebAssembly APIs
Swift
30
star
12

swiftwasm-compile-service

Web service for compling Swift to Wasm, used by Try It Now on https://swiftwasm.org
JavaScript
23
star
13

WebWorkerKit

A way of running Swift Distributed Actors in their own worker "thread"
Swift
21
star
14

swiftwasm-action

GitHub Action with SwiftWasm toolchain and SDK preinstalled
Dockerfile
16
star
15

swift-web-github-example

Demo project of SwiftWasm app using webpack
Swift
14
star
16

browser-basic-template

Basic template for browser apps built with SwiftWasm
Swift
14
star
17

Gravity

Binary code size profiler for WebAssembly
Swift
13
star
18

WebFoundation

Web-based implementation of missing SwiftWasm Foundation APIs
Swift
13
star
19

swiftwasm-sdk

This repository is no longer in use, please refer to the main repo https://github.com/swiftwasm/swift
Shell
13
star
20

blog.swiftwasm.org

SwiftWasm organization blog
Swift
12
star
21

swiftwasm-package-sdk

This repository is no longer in use, please refer to the main repo https://github.com/swiftwasm/swift
Shell
8
star
22

swiftwasm-docker

Docker images for SwiftWasm
Dockerfile
8
star
23

swift-webpack-plugin

webpack plugin for Swift
JavaScript
6
star
24

size-tracker

Utility for tracking size of `.wasm` binaries produced by latest SwiftWasm snapshots
HTML
6
star
25

setup-swiftwasm

An action to download a prebuilt SwiftWasm toolchain and add it to the PATH
JavaScript
5
star
26

homebrew-tap

🍺 Homebrew Tap for SwiftWasm-related formulae
Ruby
3
star
27

swiftwasm-wasi-stubs

Stubs for getting things to link with the WASI sdk
C
3
star
28

swift-webpack-template

JavaScript
2
star
29

swift-community-ci-playbook

2
star
30

wasi-sdk-build

Build of wasi-sdk for SwiftWasm
Makefile
2
star