Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/swiftwasm/javascriptkit
Swift framework to interact with JavaScript through WebAssembly.
https://github.com/swiftwasm/javascriptkit
javascript swift swiftwasm webassembly
Last synced: about 17 hours ago
JSON representation
Swift framework to interact with JavaScript through WebAssembly.
- Host: GitHub
- URL: https://github.com/swiftwasm/javascriptkit
- Owner: swiftwasm
- License: mit
- Created: 2020-03-04T07:10:29.000Z (almost 5 years ago)
- Default Branch: main
- Last Pushed: 2024-05-08T13:20:52.000Z (8 months ago)
- Last Synced: 2024-05-13T15:08:04.819Z (7 months ago)
- Topics: javascript, swift, swiftwasm, webassembly
- Language: Swift
- Homepage: https://swiftpackageindex.com/swiftwasm/JavaScriptKit/main/documentation/javascriptkit
- Size: 3.42 MB
- Stars: 617
- Watchers: 15
- Forks: 42
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# JavaScriptKit
![Run unit tests](https://github.com/swiftwasm/JavaScriptKit/workflows/Run%20unit%20tests/badge.svg?branch=main)
Swift framework to interact with JavaScript through WebAssembly.
## Getting started
This JavaScript code
```javascript
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
```swift
import JavaScriptKitlet 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](https://tokamak.dev)):1. Make sure that your target depends on `JavaScriptEventLoop` in your `Packages.swift`:
```swift
.target(
name: "JavaScriptKitExample",
dependencies: [
"JavaScriptKit",
.product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
]
)
```2. Add an explicit import in the code that executes **before* you start using `await` and/or `Task`
APIs (most likely in `main.swift`):```swift
import JavaScriptEventLoop
```3. Run this function **before* you start using `await` and/or `Task` APIs (again, most likely in
`main.swift`):```swift
JavaScriptEventLoop.installGlobalExecutor()
```Then you can `await` on the `value` property of `JSPromise` instances, like in the example below:
```swift
import JavaScriptKit
import JavaScriptEventLooplet alert = JSObject.global.alert.function!
let document = JSObject.global.documentprivate 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.
```diff
.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](https://swift.org/download/) 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](https://caniuse.com/#feat=wasm) 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](https://carton.dev). Add carton to your swift package dependencies:```diff
dependencies: [
+ .package(url: "https://github.com/swiftwasm/carton", from: "1.0.0"),
],
```Now you can activate the package dependency through swift:
```
swift run carton dev
```If you have multiple products in your package, you can also used the product flag:
```
swift run carton dev --product MyApp
```> [!WARNING]
> - If you already use `carton` before 0.x.x versions via Homebrew, you can remove it with `brew uninstall carton` and install the new version as a SwiftPM dependency.
> - Also please remove the old `.build` directory before using the new `carton`Legacy Installation
---
As a part of these steps
you'll install `carton` via [Homebrew](https://brew.sh/) on macOS (you can also use the
[`ghcr.io/swiftwasm/carton`](https://github.com/orgs/swiftwasm/packages/container/package/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
```2. Create a directory for your project and make it current:
```
mkdir SwiftWasmApp && cd SwiftWasmApp
```3. Initialize the project from a template with `carton`:
```
carton init --template basic
```4. Build the project and start the development server, `carton dev` can be kept running
during development:```
carton dev
```---
Open [http://127.0.0.1:8080/](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](https://github.com/swiftwasm/JavaScriptKit/tree/main/Example)
directory for more information in this more advanced use case.## Sponsoring
[Become a gold or platinum sponsor](https://github.com/sponsors/swiftwasm/) and contact maintainers to add your logo on our README on Github with a link to your site.