Getting started#
SwiftBrowser runs an iOS SwiftUI app in the browser from its unchanged sources. The app compiles to WebAssembly against an API-compatible SwiftUI shim and draws inside an iPhone frame on a desktop, or full screen on a phone. You keep building the same project in Xcode for iOS; the browser build is a second target that needs no Mac.
This page takes an existing app from zero to a deployed static site. The CLI reference has every option, and Compatibility lists what the shim supports.
Requirements#
- Node 22 or later.
- Swift 6.4, installed with swiftly
(
swiftly install 6.4.0), or any 6.4 toolchain whoseusr/binis inSWIFT_TOOLCHAIN_BIN. - The Swift SDK for WebAssembly and Binaryen's
wasm-opt.doctor --installfetches both.
Linux and macOS both work. Nothing is installed globally besides the Swift
SDK; Binaryen and cached data live in ~/.cache/swiftbrowser.
npx @swiftbrowser/cli doctor --install
doctor without --install only reports what is missing.
1. Check the app#
Point the CLI at an .xcodeproj, a folder that holds one (directly or one
level down), or a plain folder of Swift sources:
npx @swiftbrowser/cli check ~/Projects/MyApp
check lists every module the app imports as supported, rewritten or
unsupported (with the usual way around each), then compiles the app natively
against the shim. It takes seconds and builds no WebAssembly. Every error
names your file and line. Anything the shim lacks is a compile error, never a
wrong rendering at runtime.
When a project has several app targets, pick one with --target <name>.
2. Work around what is missing#
Code that cannot run in the browser goes behind the SWIFTBROWSER compilation
condition, which the CLI defines, much like #if os(iOS):
#if SWIFTBROWSER
// A stand-in for the browser.
Text("Maps are not available in the browser yet")
#else
Map(position: $position)
#endif
Xcode never defines SWIFTBROWSER, so the iOS build is unaffected.
3. Run it with live reload#
npx @swiftbrowser/cli dev ~/Projects/MyApp
The dev server opens a landing page at http://localhost:5173 with a QR code,
and serves the app at /app/?app=<Name>. Saving a Swift file rebuilds the
module and reloads the page with the app's @State carried over where the
view tree still matches.
To try it on a phone, add --host 0.0.0.0 to expose the server on your
network and scan the QR code. On a phone the app drops the iPhone frame and uses the real
screen and safe areas; in Safari, Share → Add to Home Screen runs it full
screen.
Other useful flags: --port, --open, and --verbose for the full compiler
output.
4. Build a static site#
npx @swiftbrowser/cli build ~/Projects/MyApp -o dist
build makes a release module, optimized and stripped with wasm-opt, and a
complete static site around it: a landing page, the app page at
/app/?app=<Name>, the asset catalogs and a web app manifest. A small app is
about 17 MB, 4.5 MB with brotli. Serve dist/ from any static host that sends
.wasm files as application/wasm (Cloudflare Pages, Netlify, GitHub Pages
and S3 all do).
What the CLI does to your project#
Nothing outside <project>/.swiftbrowser/, which you should add to
.gitignore. The CLI reads the Xcode project, copies the app target's sources
into a generated SwiftPM package there, adjusts the copies for the shim
(keeping line numbers, so diagnostics point at your files) and builds that.
See What happens to your project.
Configuration#
Most projects need none. When the Xcode project is not enough, or the app is
a plain folder of sources, an optional swiftbrowser.json next to it names
the sources, resources, asset catalogs, icon, ICU locales and extra
compilation conditions. See
swiftbrowser.json.
Data that persists#
What an iOS app keeps on the device, the browser keeps per app:
| On iOS | In the browser | Reset with |
|---|---|---|
UserDefaults, @AppStorage |
localStorage | clearing site data |
| SwiftData | IndexedDB, sb-swiftdata:<App> |
?swiftdata=reset |
Files in the app's home (URL.documentsDirectory, …) |
IndexedDB, sb-files:<App> |
?files=reset |
Next#
- CLI reference: commands, Foundation and ICU data, frameworks, Core ML, Swift packages.
- Compatibility: the SwiftUI and framework APIs that work.
- Architecture: how the shim, the render ops and the renderer fit together.