viewkit-cli
v0.2.0
Published
Real Safari screenshots of any URL on real iOS Simulators: the Safari app and the in-app Safari view, across iPhone sizes. One command.
Maintainers
Readme
viewkit
Real Safari screenshots of any URL, on real iOS Simulators. Both Safari surfaces that exist on iPhone — the Safari app, and the system in-app Safari view your links open in from Slack, Discord or Mail — across three iPhone sizes. One command, one gallery. Nothing emulated.
npx viewkit-cli https://your-site.comOr run it bare for the interactive wizard (paste the URL, pick devices and views with the arrow keys):
npx viewkit-cliThe npm package is viewkit-cli; the installed binary is viewkit (so after
npm i -g viewkit-cli the command is just viewkit).
Requirements
macOS with Xcode installed (the full app — it ships the iOS Simulators viewkit drives). Nothing else: no browser downloads, no accounts, no tunnels.
Usage
viewkit <url> [url2 ...] [options]
viewkit # no URL, in a terminal: interactive wizard
--only <sel> Comma list of devices, views, or profile names (default: all 6)
devices: iphone-se, iphone-15, iphone-15-pro-max
views: safari, safari-view
--out <dir> Output directory (default: ./shots)
--dark Capture in system dark mode
--config <file> JSON config (default: ./viewkit.config.json if present)
-h, --help Show helpThe two views
- safari — the Safari app: real toolbar, address bar, Dynamic Island, real safe areas.
- safari-view — the system in-app browser (
SFSafariViewController): close button, centered host, share/reload bar. Slack, Discord, Telegram, Mail and every app using the system view present exactly this — so one capture covers all of them.
Examples
viewkit https://example.com # all 6 captures
viewkit https://example.com --only iphone-15 # one device, both views
viewkit https://example.com --only safari-view # in-app view, all devices
viewkit "http://localhost:5173/?x=1" --dark # local dev server, dark modeOutput: one <profile>.png per capture plus an index.html gallery grouped
by view. Open shots/index.html to compare.
How it works
viewkit drives the iOS Simulator via xcrun simctl: boot (idempotent —
simulators stay booted between runs; first run ~1 min, rest are seconds),
pin the status bar to 9:41, set light/dark appearance, then:
safariprofiles open the URL in the Safari app (simctl openurl).safari-viewprofiles launch a tiny bundled harness app that presents the URL in a realSFSafariViewController. The harness compiles once withswiftcon first use and is cached in~/.viewkit/harness.
Readiness is detected by polling the screen until it stops changing
(baseline-aware, so a slow first paint is not mistaken for a settled page).
Device mapping falls back to newer installed models (iPhone 15 → 16 → 17), so
any recent Xcode works. xcrun simctl shutdown all when you're done.
Config file
Optional. If viewkit.config.json exists in the working directory it is
merged (CLI flags win). The wizard offers to save one.
{
"urls": ["http://localhost:5173/?tenant=dev"],
"only": "safari-view",
"out": "qa-shots",
"dark": false
}Upgrading from 0.1
Everything is a real capture now: --real is implied and --wait no longer
applies (both are accepted and ignored with a note, so old scripts keep
running). The emulated profiles (desktop engines, browser skins, per-app
WebViews) are gone — the per-app in-app browsers were all rendering the same
system Safari view anyway, which is now the safari-view profile.
