screenshots-ios
v1.0.1
Published
Capture iOS simulator screenshots from the command line. Build, install, launch with deterministic data, and screenshot — one command.
Maintainers
Readme
screenshots-ios
Capture iOS simulator screenshots from the command line.
Build, install, launch with deterministic data, and capture — one command. Designed for App Store screenshots, regression testing, and CI pipelines.
Quick Start
# Run directly with npx (no install needed)
npx screenshots-ios --context screenshots/home.json
# Or install globally
npm install -g screenshots-ios
screenshots-ios --context screenshots/home.jsonRun from your app's repo root. The tool handles the full pipeline:
- Resolve simulator device
- Boot simulator
- Build app (
xcodebuild) - Install on simulator
- Override status bar (9:41, full signal, 100% battery)
- Launch app with environment variables
- Wait for UI to settle
- Capture screenshot
Requirements
- macOS with Xcode installed
xcrun,xcodebuild, andsimctlavailable in PATH- Node.js >= 18
Usage
Single Screenshot
Create a JSON context file that describes what to capture:
{
"workspace": "MyApp.xcworkspace",
"scheme": "MyApp",
"screenshotName": "home",
"outputDir": "docs/screenshots",
"launchEnv": {
"APP_USE_PREVIEW_DATA": "1",
"APP_INITIAL_SCREEN": "home"
}
}npx screenshots-ios --context screenshots/home.jsonMultiple Screenshots
Build once, then --skip-build the rest:
npx screenshots-ios --context screenshots/home.json
npx screenshots-ios --context screenshots/settings.json --skip-build
npx screenshots-ios --context screenshots/profile.json --skip-buildList Available Simulators
npx screenshots-ios --list-devices
npx screenshots-ios --list-devices --simulator "iPhone" --os "26"CLI Flags
Flags override values from the context file:
npx screenshots-ios --context screenshots/home.json \
--skip-build \
--simulator "iPhone 17 Pro Max" \
--wait 5 \
--name home-dark \
--launch-env THEME=dark \
--openContext File Reference
All fields except scheme are optional.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| workspace | string | auto-detected | .xcworkspace path |
| project | string | auto-detected | .xcodeproj path (if no workspace) |
| scheme | string | required | Xcode build scheme |
| configuration | string | "Debug" | Build configuration |
| appName | string | auto-detected | Product name (to find .app bundle) |
| bundleId | string | from Info.plist | Bundle identifier |
| simulator | string | "iPhone 17 Pro" | Simulator device name |
| simulatorOS | string | any | Runtime filter (e.g. "26.2") |
| simulatorUdid | string | — | Exact UDID (bypasses name lookup) |
| derivedDataPath | string | "Derived/Screenshots" | xcodebuild derived data |
| outputDir | string | "docs/screenshots" | Where screenshots are saved |
| screenshotName | string | "app" | File prefix: {name}-{timestamp}.png |
| waitSeconds | number | 2 | Seconds to wait before capture |
| statusBar.enabled | boolean | true | Override status bar |
| statusBar.time | string | "09:41" | Status bar clock time |
| skipBuild | boolean | false | Skip xcodebuild |
| cleanBuild | boolean | false | Run clean build |
| openAfterCapture | boolean | false | Open screenshot in Preview |
| testBeforeCapture | boolean | false | Run tests first |
| testScheme | string | same as scheme | Test scheme |
| launchEnv | object | {} | Env vars passed to the app |
| launchArgs | string[] | [] | Launch arguments |
CLI Reference
Usage: screenshots-ios [options]
Required:
--scheme <name> Build scheme (or set in context file)
Container:
--workspace <path> .xcworkspace path
--project <path> .xcodeproj path
--context <path> JSON context file
Build:
--configuration <name> Build configuration (default: Debug)
--derived-data <path> DerivedData path
--skip-build Reuse existing build
--clean-build Clean before building
Simulator:
--simulator <name> Device name (default: iPhone 17 Pro)
--os <value> Runtime filter (e.g. "26.2")
--udid <uuid> Exact simulator UDID
Output:
--name <value> Screenshot file prefix
--output-dir <path> Output directory
--wait <seconds> Delay before capture
--open Open after capture
App context:
--launch-env KEY=VALUE Environment variable (repeatable)
--launch-arg <arg> Launch argument (repeatable)
--app-name <name> Product name
--bundle-id <id> Bundle identifier
Status bar:
--status-bar-time <HH:MM> Clock time (default: 09:41)
--no-status-bar Disable override
Testing:
--test-before-capture Run tests first
--test-scheme <name> Test scheme
General:
--list-devices List available simulators
-h, --help Show helpWiring Your App for Deterministic Screenshots
The tool handles build/install/launch/capture. Your app handles showing the right screen with the right data. Here's the contract:
1. Preview Mode
Read an environment variable at launch to switch to deterministic data:
let usePreview = ProcessInfo.processInfo.environment["APP_USE_PREVIEW_DATA"] == "1"2. Screen Navigation
Read an environment variable to jump to a specific screen:
@State private var selectedTab: Tab = {
if let tab = ProcessInfo.processInfo.environment["APP_INITIAL_TAB"] {
return Tab(rawValue: tab) ?? .home
}
return .home
}()3. In-Memory Repositories
Replace real data sources with seeded in-memory implementations:
actor PreviewRepository: SomeRepository {
private var items: [Item]
init(seed: [Item]) { self.items = seed }
func fetchAll() async -> [Item] { items }
}4. Static Seed Data
Use hardcoded data with fixed IDs — no random values, no Date():
enum PreviewData {
static let items: [Item] = [
Item(id: "fixed-uuid-1", name: "Groceries", amount: 25000),
Item(id: "fixed-uuid-2", name: "Transport", amount: 37000),
]
}5. Modal Screens (Optional)
For screens triggered by user actions, add env var hooks:
.onAppear {
if ProcessInfo.processInfo.environment["APP_OPEN_REVIEW_FLOW"] == "1" {
showReviewFlow = true
}
}Environment Variable Flow
context.json → launchEnv → SIMCTL_CHILD_ prefix → simulator → ProcessInfo.processInfo.environmentThe tool automatically adds the SIMCTL_CHILD_ prefix. Your app reads the original key name.
Recommended Project Layout
your-app/
├── screenshots/ # Context files (one per screenshot)
│ ├── capture-all.sh # Batch script
│ ├── home.json
│ ├── settings.json
│ └── profile.json
├── docs/screenshots/ # Output
├── MyApp.xcworkspace
└── ...Batch Script Template
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$REPO_ROOT"
CONTEXTS=(home settings profile)
for i in "${!CONTEXTS[@]}"; do
ctx="${CONTEXTS[$i]}"
skip=""
[[ "$i" -gt 0 ]] && skip="--skip-build"
npx screenshots-ios --context "$SCRIPT_DIR/${ctx}.json" $skip
doneLicense
MIT
