Skip to content
  • Sponsor swiftwasm/JavaScriptKit

  • Notifications You must be signed in to change notification settings
  • Fork 51

Commit 53ad139

Browse files
authoredMar 14, 2025
Merge pull request #294 from swiftwasm/yt/tutorials
Add Hello world tutorial
2 parents 2a8b343 + ae38d06 commit 53ad139

19 files changed

+330
-234
lines changed
 

Diff for: ‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ xcuserdata/
99
.vscode
1010
Examples/*/Bundle
1111
Examples/*/package-lock.json
12+
/Package.resolved

Diff for: ‎Package.swift

+6
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,9 @@ let package = Package(
8383
),
8484
]
8585
)
86+
87+
if Context.environment["JAVASCRIPTKIT_USE_DOCC_PLUGIN"] != nil {
88+
package.dependencies.append(
89+
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.4.0")
90+
)
91+
}

Diff for: ‎README.md

+23-234
Original file line numberDiff line numberDiff line change
@@ -4,262 +4,51 @@
44

55
Swift framework to interact with JavaScript through WebAssembly.
66

7-
## Getting started
7+
## Quick Start
88

9-
This JavaScript code
9+
Check out the [Hello World](https://swiftpackageindex.com/swiftwasm/JavaScriptKit/main/tutorials/javascriptkit/hello-world) tutorial for a step-by-step guide to getting started.
1010

11-
```javascript
12-
const alert = window.alert;
13-
const document = window.document;
11+
## Overview
1412

15-
const divElement = document.createElement("div");
16-
divElement.innerText = "Hello, world";
17-
const body = document.body;
18-
body.appendChild(divElement);
13+
JavaScriptKit provides a seamless way to interact with JavaScript from Swift code when compiled to WebAssembly. It allows Swift developers to:
1914

20-
const pet = {
21-
age: 3,
22-
owner: {
23-
name: "Mike",
24-
},
25-
};
26-
27-
alert("JavaScript is running on browser!");
28-
```
29-
30-
Can be written in Swift using JavaScriptKit
15+
- Access JavaScript objects and functions
16+
- Create closures that can be called from JavaScript
17+
- Convert between Swift and JavaScript data types
18+
- Use JavaScript promises with Swift's `async/await`
19+
- Work with multi-threading
3120

3221
```swift
3322
import JavaScriptKit
3423

24+
// Access global JavaScript objects
3525
let document = JSObject.global.document
3626

37-
var divElement = document.createElement("div")
38-
divElement.innerText = "Hello, world"
39-
_ = document.body.appendChild(divElement)
40-
41-
struct Owner: Codable {
42-
let name: String
43-
}
44-
45-
struct Pet: Codable {
46-
let age: Int
47-
let owner: Owner
48-
}
49-
50-
let jsPet = JSObject.global.pet
51-
let swiftPet: Pet = try JSValueDecoder().decode(from: jsPet)
52-
53-
_ = JSObject.global.alert!("Swift is running in the browser!")
54-
```
55-
56-
### `async`/`await`
57-
58-
Starting with SwiftWasm 5.5 you can use `async`/`await` with `JSPromise` objects. This requires
59-
a few additional steps though (you can skip these steps if your app depends on
60-
[Tokamak](https://tokamak.dev)):
61-
62-
1. Make sure that your target depends on `JavaScriptEventLoop` in your `Packages.swift`:
63-
64-
```swift
65-
.target(
66-
name: "JavaScriptKitExample",
67-
dependencies: [
68-
"JavaScriptKit",
69-
.product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
70-
]
71-
)
72-
```
73-
74-
2. Add an explicit import in the code that executes **before* you start using `await` and/or `Task`
75-
APIs (most likely in `main.swift`):
76-
77-
```swift
78-
import JavaScriptEventLoop
79-
```
80-
81-
3. Run this function **before* you start using `await` and/or `Task` APIs (again, most likely in
82-
`main.swift`):
83-
84-
```swift
85-
JavaScriptEventLoop.installGlobalExecutor()
86-
```
87-
88-
Then you can `await` on the `value` property of `JSPromise` instances, like in the example below:
89-
90-
```swift
91-
import JavaScriptKit
92-
import JavaScriptEventLoop
93-
94-
let alert = JSObject.global.alert.function!
95-
let document = JSObject.global.document
96-
97-
private let jsFetch = JSObject.global.fetch.function!
98-
func fetch(_ url: String) -> JSPromise {
99-
JSPromise(jsFetch(url).object!)!
100-
}
101-
102-
JavaScriptEventLoop.installGlobalExecutor()
103-
104-
struct Response: Decodable {
105-
let uuid: String
106-
}
107-
108-
var asyncButtonElement = document.createElement("button")
109-
asyncButtonElement.innerText = "Fetch UUID demo"
110-
asyncButtonElement.onclick = .object(JSClosure { _ in
111-
Task {
112-
do {
113-
let response = try await fetch("https://httpbin.org/uuid").value
114-
let json = try await JSPromise(response.json().object!)!.value
115-
let parsedResponse = try JSValueDecoder().decode(Response.self, from: json)
116-
alert(parsedResponse.uuid)
117-
} catch {
118-
print(error)
119-
}
120-
}
27+
// Create and manipulate DOM elements
28+
var div = document.createElement("div")
29+
div.innerText = "Hello from Swift!"
30+
_ = document.body.appendChild(div)
12131

32+
// Handle events with Swift closures
33+
var button = document.createElement("button")
34+
button.innerText = "Click me"
35+
button.onclick = .object(JSClosure { _ in
36+
JSObject.global.alert!("Button clicked!")
12237
return .undefined
12338
})
124-
125-
_ = document.body.appendChild(asyncButtonElement)
126-
```
127-
128-
### `JavaScriptEventLoop` activation in XCTest suites
129-
130-
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.
131-
132-
```diff
133-
.testTarget(
134-
name: "MyAppTests",
135-
dependencies: [
136-
"MyApp",
137-
+ "JavaScriptEventLoopTestSupport",
138-
]
139-
)
140-
```
141-
142-
Linking this module automatically activates JS event loop based global executor by calling `JavaScriptEventLoop.installGlobalExecutor()`
143-
144-
145-
## Requirements
146-
147-
### For developers
148-
149-
- macOS 11 and Xcode 13.2 or later versions, which support Swift Concurrency back-deployment.
150-
To use earlier versions of Xcode on macOS 11 you'll have to
151-
add `.unsafeFlags(["-Xfrontend", "-disable-availability-checking"])` in `Package.swift` manifest of
152-
your package that depends on JavaScriptKit. You can also use Xcode 13.0 and 13.1 on macOS Monterey,
153-
since this OS does not need back-deployment.
154-
- [Swift 5.5 or later](https://swift.org/download/) and Ubuntu 18.04 if you'd like to use Linux.
155-
Other Linux distributions are currently not supported.
156-
157-
### For users of apps depending on JavaScriptKit
158-
159-
Any recent browser that [supports WebAssembly](https://caniuse.com/#feat=wasm) and required
160-
JavaScript features should work, which currently includes:
161-
162-
- Edge 84+
163-
- Firefox 79+
164-
- Chrome 84+
165-
- Desktop Safari 14.1+
166-
- Mobile Safari 14.8+
167-
168-
If you need to support older browser versions, you'll have to build with
169-
the `JAVASCRIPTKIT_WITHOUT_WEAKREFS` flag, passing `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` flags
170-
when compiling. This should lower browser requirements to these versions:
171-
172-
- Edge 16+
173-
- Firefox 61+
174-
- Chrome 66+
175-
- (Mobile) Safari 12+
176-
177-
Not all of these versions are tested on regular basis though, compatibility reports are very welcome!
178-
179-
## Usage in a browser application
180-
181-
The easiest is to start with [Examples](/Examples) which has JavaScript glue runtime.
182-
183-
Second option is to get started with JavaScriptKit in your browser app is with [the `carton`
184-
bundler](https://carton.dev). Add carton to your swift package dependencies:
185-
186-
```diff
187-
dependencies: [
188-
+ .package(url: "https://github.com/swiftwasm/carton", from: "1.1.3"),
189-
],
190-
```
191-
192-
Now you can activate the package dependency through swift:
193-
194-
```
195-
swift run carton dev
39+
_ = document.body.appendChild(button)
19640
```
19741

198-
If you have multiple products in your package, you can also used the product flag:
199-
200-
```
201-
swift run carton dev --product MyApp
202-
```
42+
Check out the [examples](https://github.com/swiftwasm/JavaScriptKit/tree/main/Examples) for more detailed usage.
20343

204-
> [!WARNING]
205-
> - 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.
206-
> - Also please remove the old `.build` directory before using the new `carton`
44+
## Contributing
20745

208-
<details><summary>Legacy Installation</summary>
209-
210-
---
211-
212-
As a part of these steps
213-
you'll install `carton` via [Homebrew](https://brew.sh/) on macOS (you can also use the
214-
[`ghcr.io/swiftwasm/carton`](https://github.com/orgs/swiftwasm/packages/container/package/carton)
215-
Docker image if you prefer to run the build steps on Linux). Assuming you already have Homebrew
216-
installed, you can create a new app that uses JavaScriptKit by following these steps:
217-
218-
1. Install `carton`:
219-
220-
```
221-
brew install swiftwasm/tap/carton
222-
```
223-
224-
If you had `carton` installed before this, make sure you have version 0.6.1 or greater:
225-
226-
```
227-
carton --version
228-
```
229-
230-
2. Create a directory for your project and make it current:
231-
232-
```
233-
mkdir SwiftWasmApp && cd SwiftWasmApp
234-
```
235-
236-
3. Initialize the project from a template with `carton`:
237-
238-
```
239-
carton init --template basic
240-
```
241-
242-
4. Build the project and start the development server, `carton dev` can be kept running
243-
during development:
244-
245-
```
246-
carton dev
247-
```
248-
249-
---
250-
251-
</details>
252-
253-
Open [http://127.0.0.1:8080/](http://127.0.0.1:8080/) in your browser and a developer console
254-
within it. You'll see `Hello, world!` output in the console. You can edit the app source code in
255-
your favorite editor and save it, `carton` will immediately rebuild the app and reload all
256-
browser tabs that have the app open.
46+
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to the project.
25747

25848
## Sponsoring
25949

26050
[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.
26151

262-
26352
<a href="https://www.emergetools.com/">
26453
<img src="https://github.com/swiftwasm/swift/raw/swiftwasm-distribution/assets/sponsors/emergetools.png" width="30%">
26554
</a>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# JavaScript Environment Requirements
2+
3+
## Required JavaScript Features
4+
5+
The JavaScript package produced by the JavaScriptKit packaging plugin requires the following JavaScript features:
6+
7+
- [`FinalizationRegistry`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry#browser_compatibility)
8+
- [WebAssembly BigInt to i64 conversion in JS API](https://caniuse.com/wasm-bigint)
9+
10+
## Browser Compatibility
11+
12+
These JavaScript features are supported in the following browsers:
13+
14+
- Chrome 85+ (August 2020)
15+
- Firefox 79+ (July 2020)
16+
- Desktop Safari 14.1+ (April 2021)
17+
- Mobile Safari 14.5+ (April 2021)
18+
- Edge 85+ (August 2020)
19+
- Node.js 15.0+ (October 2020)
20+
21+
Older browsers will not be able to run applications built with JavaScriptKit unless polyfills are provided.
22+
23+
## Handling Missing Features
24+
25+
### FinalizationRegistry
26+
27+
When using JavaScriptKit in environments without `FinalizationRegistry` support, you can:
28+
29+
1. Build with the opt-out flag: `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS`
30+
2. Then manually manage memory by calling `release()` on all `JSClosure` instances:
31+
32+
```swift
33+
let closure = JSClosure { args in
34+
// Your code here
35+
return .undefined
36+
}
37+
38+
// Use the closure...
39+
40+
// Then release it when done
41+
#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
42+
closure.release()
43+
#endif
44+
```
45+
46+
### WebAssembly BigInt Support
47+
48+
If you need to work with 64-bit integers in JavaScript, ensure your target environment supports WebAssembly BigInt conversions. For environments that don't support this feature, you'll need to avoid importing `JavaScriptBigIntSupport`

0 commit comments

Comments
 (0)