@xsim/cli
XSim
XSim is a small Node.js CLI for building, seeding, and replaying iOS Simulator profiles from YAML.
It helps you:
- scaffold a profile from an Xcode project or workspace
- seed simulator state with files, media, permissions, location, push notifications, clipboard content, and app data
- launch a fresh simulator from that profile
- record new AXe launch steps back into the profile
- clone a booted simulator into a reusable profile
- tear down generated simulators when you are done
Requirements
- macOS
- Node.js 18 or newer
- Xcode command line tools
xcodebuildandxcrunavailable onPATH- AXe available on
PATH, or Homebrew soxsim initcan install it for you swiftonPATHif you want to usexsim record
xsim init checks for AXe and installs it with Homebrew if it is missing.
Quick Start
Choose one of these two setup paths. Most people will use the default profile, so the examples below assume default unless noted otherwise.
Option 1: Install from npm
Install the package globally:
npm install -g @xsim/cli
Then verify the CLI is available:
xsim --help
From there, create a profile and launch it:
xsim init .
xsim launch
If you omit the profile name, default is used.
Option 2: Clone the repo locally
Use this path if you want to make changes and test them from your own checkout.
git clone <repo-url>
cd xsim-cli
npm install
npm run link
That makes the local xsim command available globally from this checkout.
To remove the global symlink later:
npm run unlink
Once linked, you can create and launch a profile the same way:
xsim init .
xsim launch
That will search upward and downward from the path you pass to find the Xcode project or workspace, detect a default scheme, and write a profile under .xsim/default/.
What XSim Creates
An initialized profile lives alongside your project:
your-project/
.xsim/
default/
config.yml
files/
media/
DerivedData/
Some commands may add more folders:
.xsim/
default/
appData/
apps/
These are used when cloning simulators or restoring captured app data.
How It Works
At a high level, xsim launch does this:
- Finds the
.xsim/<profile>directory. - Reads
config.yml. - Resolves the Xcode project and scheme.
- Creates a new simulator with a generated display name like
XSIM Default #ABCDE. - Boots the simulator.
- Applies appearance, locale, location, and status bar overrides.
- Applies keychain, media, files, app data, permissions, clipboard, and push notification seed data.
- Builds the app from the detected scheme or installs a copied app bundle.
- Launches the app.
- Runs AXe launch steps, if any are configured.
The command prints the new simulator UUID on success.
Commands
xsim init <path> [name]
Creates a new profile for a project.
What it does:
- discovers the Xcode project or workspace starting at
<path> - resolves a default scheme with
xcodebuild -list -json - creates
.xsim/<name>/ - writes
config.yml - creates
files/,media/, andDerivedData/
Options:
-f, --forceoverwrite an existing profile
Examples:
xsim init .
xsim init ~/Projects/MyApp staging
xsim init ~/Projects/MyApp staging --force
xsim launch [name]
Creates, seeds, and launches a simulator from a profile.
What it does:
- finds
.xsim/<name> - reads the profile config
- creates a simulator using the configured or default device/runtime
- applies all configured seed blocks
- builds the app from the project scheme, or installs a cloned app bundle if the profile points to one
- launches the app
- runs any AXe batch steps in
launchSteps
Options:
-q, --quietsuppress progress output and print only the simulator UUID
Examples:
xsim launch
xsim launch staging
xsim launch staging --quiet
xsim clone [name]
Clones a booted simulator into a new profile.
What it does:
- prompts for a booted simulator if more than one is available
- picks a running app when possible, otherwise a user app
- tries to reuse an existing
.xsimprofile that matches the source simulator name - copies the source profile’s
files/andmedia/folders when a matching profile exists - captures the selected app’s data container and app group containers into
appData/ - copies the app bundle into
apps/when no matching source profile exists - writes a new profile config
Options:
-q, --quietsuppress progress output
Examples:
xsim clone
xsim clone debug-profile
xsim record [name]
Launches a profile, then records new simulator interactions as AXe steps.
What it does:
- locates the profile root
- lets you choose a profile if one is not provided
- boots a seeded simulator
- starts a lightweight recorder that listens for mouse and keyboard events while the Simulator is frontmost
- replays any existing
launchStepsfirst - appends newly captured steps to
config.yml
Options:
-q, --quietsuppress progress output
Notes:
- you need
swiftinstalled - the terminal or shell must have Accessibility permissions so event capture can work
Example:
xsim record
xsim teardown [uuid]
Deletes simulator devices created by XSim.
What it does:
- if you pass a UUID, it shuts down and deletes that simulator and removes its device directory
- if you omit the UUID, it deletes every simulator whose name matches
XSIM <profile> #ABCDE
Examples:
xsim teardown
xsim teardown 12345678-1234-1234-1234-123456789ABC
Profile Layout
The profile is just a directory with YAML and seed assets.
.xsim/<profile>/
config.yml
files/
media/
DerivedData/
appData/
apps/
config.ymlcontrols simulator settings and seed behavior.files/is copied into the simulator’s iCloud DriveXSim/<profile>/folder.media/is imported withsimctl addmedia.DerivedData/is used as the build output directory.appData/stores captured app data snapshots for restore.apps/stores copied app bundles when cloning a simulator without an existing source profile.
Configuration Reference
config.yml is written in YAML and merged with a default schema.
Here is a simplified example:
version: 1
name: default
project:
path: /absolute/path/to/MyApp.xcodeproj
kind: xcodeproj
root: /absolute/path/to
scheme: MyApp
simulator:
displayName: XSIM Default
device: latest
runtime: latest
appearance: light
locale: en_US
language: en
permissions:
photos: true
statusBar:
time: "09:41"
dataNetwork: wifi
wifiMode: active
batteryState: charged
batteryLevel: 100
location:
latitude: 37.3349
longitude: -122.00902
keychain:
reset: true
rootCerts:
- certs/root-ca.cer
pushNotifications:
- bundleId: com.example.myapp
payload:
aps:
alert:
title: Hello
body: Seeded push notification
clipboard:
text: Hello from XSim
appData:
- bundleId: com.example.myapp
source: appData/com.example.myapp
launchSteps:
steps:
- tap --label "Continue"
- sleep 1
- type "Hello from XSim"
options:
waitTimeout: 2
continueOnError: false
project
pathpoints to the discovered.xcodeprojor.xcworkspacekindisxcodeproj,xcworkspace, orapprootis the directory that contains the project bundleschemeis the selected Xcode scheme
simulator
Supported fields include:
displayNamedeviceruntimeappearancelocalelanguage
The display name is turned into a real simulator name like XSIM Default #ABCDE.
permissions
Permissions are mapped to simctl privacy.
You can use:
trueto grantfalseto revokenullto skip
Example:
permissions:
camera: true
photos: false
statusBar
statusBar is passed to simctl status_bar.
You can use direct overrides:
statusBar:
time: "09:41"
dataNetwork: wifi
wifiMode: active
batteryLevel: 100
You can also use:
statusBar:
clear: true
override:
time: "12:00"
location
location is passed to simctl location.
Supported forms include:
location:
latitude: 37.3349
longitude: -122.00902
location:
coordinate: "37.3349,-122.00902"
location:
scenario: "gpx-route-name"
keychain
Controls simulator keychain seeding.
Supported fields:
resetrootCertscerts
Example:
keychain:
reset: true
rootCerts:
- certs/root-ca.cer
certs:
- certs/client.cer
pushNotifications
Each entry can provide a bundle ID and either inline payload data or a file path.
Examples:
pushNotifications:
- bundleId: com.example.myapp
payload:
aps:
alert:
title: Hello
body: World
pushNotifications:
- file: push/welcome.apns
clipboard
Sets the initial pasteboard contents.
Example:
clipboard:
text: Hello from XSim
You can also use a plain string:
clipboard: Hello from XSim
appData
Two modes are supported:
- point to a snapshot directory created by
xsim clone - point to a
.xcappdatapackage
Examples:
appData:
- bundleId: com.example.myapp
source: appData/com.example.myapp
appData:
- path: snapshots/MyApp.xcappdata
launchSteps
Launch steps are run through AXe after the app launches.
You can provide:
steps: an array of inline AXe batch stepsfile: a batch file pathoptions: additional AXe batch flags
Example:
launchSteps:
steps:
- tap --label "Continue"
- sleep 1
- type "hello"
options:
waitTimeout: 2
continueOnError: false
Recorded steps are appended to the existing launchSteps.steps array.
Examples
Create a new profile
xsim init .
Launch it
xsim launch
Seed a different simulator size or runtime
simulator:
device: iPhone 16 Pro
runtime: latest
Add a root certificate and clipboard text
keychain:
rootCerts:
- certs/root-ca.cer
clipboard:
text: "Logged in as demo@example.com"
Seed a location and a push notification
location:
latitude: 37.3349
longitude: -122.00902
pushNotifications:
- bundleId: com.example.myapp
payload:
aps:
alert:
title: Welcome
body: Your profile is ready
Record a fresh flow
xsim record
Then interact with the app, press Enter in the terminal when finished, and the new AXe steps are appended to config.yml.
Notes and Limits
xsim launchexpects a valid Xcode project, workspace, or app bundle path in the profile config.xsim recordonly captures interactions while the Simulator is frontmost.xsim cloneandxsim teardownwork against simulators managed by CoreSimulator.- If a configured path is relative, XSim resolves it relative to the profile directory.
Contributing
See CONTRIBUTING.md for development guidelines.
License
This project is licensed under the MIT License. See LICENSE.