CLI reference
Every xcross command, subcommand, flag and default, taken directly from the CLI source.
The executable is invoked as xcross <command> [subcommand] [options]. flutter, sdk
and ide are parent commands that only dispatch to their subcommands, so xcross flutter
on its own prints usage. Add --help to any level (xcross flutter run --help).
Global options
| Flag | Abbr | Default | Description |
|---|---|---|---|
--verbose | -v | off | Verbose output (show every command and tool line). |
-v is a global flag on the runner and is written at the end of xcross command examples
(xcross tunnel -v). xcross flutter run additionally accepts its own -v after the
subcommand, which does the same thing for that run.
Command summary
| Command | Description |
|---|---|
xcross setup | Install or verify host requirements |
xcross sdk | Manage the xcross Darwin Swift SDK. |
xcross sdk install <Xcode.xip> | Extract a host-neutral Darwin Swift SDK from an Xcode.xip. |
xcross auth | Save App Store Connect API key credentials or sign in with Apple ID for the native (no-Swift) signing pipeline. |
xcross doctor | Check build and run requirements without building or running. |
xcross config | Create, inspect, and validate xcross configuration. |
xcross config show | Print the selected xcross configuration. |
xcross config validate | Validate the selected xcross configuration. |
xcross tunnel | Mount the Developer Disk Image and start the iOS 17+ RSD tunnel over USB. Add --wifi for wireless setup. |
xcross flutter | Build and run Flutter iOS apps without Xcode. |
xcross flutter run | Build, install, and run a Flutter iOS app on a device. |
xcross flutter build | Build a Flutter iOS .app without Xcode. |
xcross flutter dap | Internal. Debug Adapter Protocol server for IDE Run & Debug buttons. |
xcross ide | Set up editor integration for Run & Debug on an iOS device. |
xcross ide vscode | Set up .vscode/ so F5, Restart and Hot Reload run on an iOS device. |
xcross ide idea | Set up a JetBrains DAP run config so Debug runs on an iOS device. |
xcross completion | Print a shell completion script for xcross. |
Commands marked Internal are hidden from xcross --help and are spawned by other tools
on your behalf. They are documented here for completeness only - do not call them directly
or script against them, since their input and output contracts can change without notice.
setup
Install or verify everything the host needs before xcross can build: the LLVM/Clang
toolchain, Swift’s own build dependencies, the USB and device utilities, and
pymobiledevice3. It is the first command you run after installing xcross, and it is
safe to re-run at any time - every step is idempotent.
xcross setup
| Flag | Description |
|---|---|
--refresh-script | Re-download the configured remote setup script and update the cache without executing it. No-op unless a remote script is configured. |
Swift is always yours to install
Before doing anything, setup checks for a usable swift on PATH and stops with a
platform-specific hint if it is missing. xcross never installs Swift on any host - the
packages it does install are Swift’s dependencies, not Swift itself.
| Host | Where to get Swift |
|---|---|
| Windows | https://www.swift.org/install/windows/, then open a new terminal |
| macOS | Xcode, or the toolchain installer at https://www.swift.org/install/macos/ |
| Linux | https://www.swift.org/install/linux/ (swiftly is the easiest route) |
Failing here is deliberate: it is far cheaper than failing after a package-manager transaction or an hours-long SDK extraction.
Linux
Setup detects your package manager on PATH - apt-get, dnf, or pacman. If exactly
one is present it is used silently. If several are present, or none is, you are prompted to
pick one; without a TTY it fails and prints the manual install command for each candidate
instead of hanging.
It then caches your sudo credential once, so the install runs without interrupting for a
password, and installs the requirement set for your distro family: clang, lld, llvm,
Python 3 with pip/venv, usbmuxd, usbutils, libimobiledevice-utils, usbip, and
the Swift build dependencies (pkg-config, zlib, libxml2, ncurses, z3, gnupg,
libcurl, gcc/g++, and the matching development headers). The package names differ per
family and are spelled the way each distro spells them.
Install commands are tried in order, most tolerant first, because package sets drift between releases of the same family:
| Manager | Attempts |
|---|---|
| apt | apt-get install -y <known packages>, then apt-get update && apt-get install |
| dnf | --skip-unavailable, then --setopt=strict=0, then plain dnf install |
| pacman | pacman -S --needed --noconfirm, then pacman -Syu --needed --noconfirm |
apt and pacman abort a whole transaction over a single unknown package name, so names this
host has never heard of are filtered out beforehand. The apt retry exists because a mirror
that has rotated to a newer point release still advertises the old .deb in your cached
index, and refreshing that index is the documented fix.
NoteThe package lists are deliberately neither minimal nor one-to-one across distros. Arch
ships headers inside the main package, Fedora never versioned some -devel packages, and
Debian’s libstdc++-N-dev version differs per release - so unversioned metapackages stand
in where needed.
The ld64.lld fix
ld64.lld is the linker that produces the iOS Mach-O binary, and versions below a known
threshold emit broken Objective-C selector stubs. Distros make this awkward: Ubuntu 24.04
pairs a default lld 18 with an lld-19 sitting beside it in the same archive, and some
distros only ship versioned names at all.
Setup handles that automatically. It probes the ld64.lld on PATH for the selector-stub
defect; if it is defective or absent, it looks for a new enough versioned lld-<N> in your
apt index, installs it, then symlinks /usr/local/bin/ld64.lld at the newest
/usr/bin/ld64.lld-<N> so plain PATH lookups find the good one. If that stable path
already exists and is not an xcross-managed symlink, it is left untouched and you get a
warning telling you how to order PATH yourself.
macOS
Homebrew is required; setup stops with a link to https://brew.sh if brew is missing. It
runs brew install lld llvm, then ensures pipx and pymobiledevice3. Swift comes from
Xcode or the toolchain installer, as above.
Windows
Nothing is installed by a package manager. Setup verifies that flutter, swift, clang,
clang++, llvm-ar and ld64.lld are on PATH and fails with the list of what is
missing, then installs pymobiledevice3. Install Flutter, Swift, and the official LLVM
Windows toolchain yourself first.
pipx and pymobiledevice3
Every host ends at the same place: pymobiledevice3, the library xcross uses for device
discovery, installation, tunnels and port forwarding.
On PEP 668 distros a plain pip install is refused, so setup uses pipx, which also keeps
pymobiledevice3 in its own virtualenv instead of polluting system Python. pipx itself is
installed by preference from your package manager (or Homebrew), falling back to
python3 -m pip install --user --break-system-packages pipx, then plain --user. After
installing, pipx ensurepath appends its bin directory to your shell profile - and if that
fails it is only a warning, because xcross looks in ~/.local/bin regardless.
Verification
After installing, setup re-checks that swift, clang, clang++, llvm-ar and ld64.lld
resolve, and fails with the missing list if any do not. The lookup reaches beyond plain
PATH into Homebrew’s keg-only LLVM and Debian’s versioned prefixes, and it explicitly
refuses swiftly’s ld64.lld shim, which cannot link for iOS.
Replacing setup entirely
A configured setup script takes over completely: when
setup is set in your configuration, xcross setup runs that script instead of any of the
above and reports Configured setup script completed. This is how a managed fleet or a CI
image provisions hosts its own way.
The value is an absolute local path or an http(s) URL.
- A local script runs directly - via
/bin/sh, orpowershell -NoProfile -Fileon Windows. - A remote script is downloaded once with a 30 second timeout, hashed with SHA-256, and stored under that hash in the setup-script cache, with a pointer file keyed by a hash of the URL.
- Later runs reuse the cached copy only when its contents still hash to the recorded digest, so a corrupted or tampered cache entry is discarded rather than executed.
xcross setup --refresh-scriptre-downloads and re-caches without running anything, which is the safe way to roll out a new script.
Cache locations:
| Host | Setup script cache |
|---|---|
| Windows | %LOCALAPPDATA%\xcross\cache\setup-scripts |
| Linux / macOS | $XDG_CACHE_HOME/xcross/setup-scripts, falling back to ~/.cache/xcross/setup-scripts |
Cache writes are atomic, and download failures report a URL stripped of any credentials or query string.
sdk install
Extract a host-neutral Darwin Swift SDK from an Xcode.xip. Run once per Xcode archive.
xcross sdk install <path-to-Xcode.xip>
The path is a positional argument, not a flag; there are no options. The command deletes
any previous SDK at the install directory, extracts the required iOS subset (iPhoneOS
SDKs, frameworks, private frameworks, Swift and clang toolchain resources), replaces
Xcode’s clang builtin headers with ones matching your host Swift, and writes the Swift
artifact-bundle metadata (swift-sdk.json, toolset.json, info.json).
xcross sdk install C:\Downloads\Xcode.xip
NoteExtraction fails if the archive does not contain a versioned iPhoneOS SDK. Use a
complete Xcode.xip from https://xcodereleases.com/, not a command line tools package.
auth
Save App Store Connect API key credentials or sign in with Apple ID for the native
(no-Swift) signing pipeline. Passing any of --issuer-id, --key-id or --private-key
selects API key mode; otherwise xcross performs an Apple ID login and prompts for
anything not supplied.
| Flag | Value | Description |
|---|---|---|
--issuer-id | id | App Store Connect API “Issuer ID” (one per team). |
--key-id | id | The API key’s “Key ID”, shown next to it in App Store Connect. |
--private-key | path | Path to the downloaded AuthKey_<keyId>.p8 file. |
--apple-id | Use Apple ID/password login. If omitted, xcross prompts. | |
--password | password | Apple ID password (optional; prompted if omitted). |
--adi-library-dir | path | Directory containing libCoreADI.so and libstoreservicescore.so for Apple ID login. Defaults to the xcross config adi-libs directory. On x86_64, missing libs are fetched from the Apple Music APK. |
xcross auth --apple-id you@example.com
xcross auth --issuer-id <uuid> --key-id <id> --private-key /path/to/AuthKey.p8
The API key flags and --apple-id are mutually exclusive, and --adi-library-dir only
applies to Apple ID login. Saving an API key clears any stored Apple ID session so the
active mode is never ambiguous. Apple ID login prompts for a 2FA code, then asks which
Developer Services team to use when more than one active team exists.
doctor
Check whether the current Linux or Windows machine is ready to build and run an xcross project without executing a build, install or launch:
xcross doctor
The command performs read-only checks for:
- supported host platform and required Swift, Clang, LLVM and linker tools
- the installed Darwin SDK and its compatibility with the active Swift toolchain
- the current Flutter or Compose project, including Flutter SDK and package configuration when applicable
- saved Apple ID or App Store Connect authentication
pymobiledevice3, connected devices and the iOS 17 minimum version
Each check prints a green success, yellow warning or red failure marker. Resolved filesystem paths appear on a dimmed line below the check. Missing project or device context is a warning, while failed requirements produce a nonzero exit code. Doctor does not install dependencies, warm caches, modify the project, build an app or communicate with a device beyond read-only discovery and version probes.
There are no options other than the global --verbose and generated --help flags.
config
Create, inspect, and validate the xcross configuration file.
xcross config # interactive TUI editor (requires a TTY)
xcross config show # print the selected configuration
xcross config validate # validate the selected configuration
The configuration is selected from $XCROSS_CONFIG, then config.yaml, then config.yml
in the host config directory. When it exists, it overrides tool locations, toolchain
directories, install roots, allowlisted environment variables, the setup script, and which
commands are exposed at all. show and validate fail when no configuration is found; the
bare command fails without a TTY.
See Configuration for every key, the discovery rules, and the runtime overlay.
tunnel
Mount the Developer Disk Image and start the iOS 17+ RSD tunnel (mounter auto-mount + lockdown start-tunnel + tunneld). Requires sudo on POSIX or an Administrator terminal on Windows.
xcross tunnel
xcross tunnel --wifi
| Flag | Default | Description |
|---|---|---|
--wifi | off | Prepare a wireless device. Uses USB lockdown RemotePairing when a phone is attached, otherwise reconnects saved pairings and falls back to iOS 27+ device-initiated pairing. |
Plain xcross tunnel is USB-only. With --wifi, USB setup is preferred when available;
without USB, saved devices are tried before xcross advertises a fresh pair-host. See
Run over Wi-Fi for the full flow. Long-lived tunnel processes stay
running after the command exits.
flutter run
Build, install, and run a Flutter iOS app on a device. Always a debug (JIT) build, always
launched with hot reload when the required frontend_server artifacts are available.
| Flag | Abbr | Default | Description |
|---|---|---|---|
--target | -t | lib/main.dart | The main entry-point file of the application. |
--flavor | Build a custom app flavor (sets FLUTTER_APP_FLAVOR). | ||
--dart-define | -D | Pass a KEY=VALUE define to the Dart compiler. Repeatable. | |
--dart-define-from-file | Load dart-defines from a .json or .env file. Repeatable. | ||
--[no-]pub | on | Run “flutter pub get” before building. | |
--device-id | -d | Target device id or name (flutter-style). | |
--udid | -u | Target device UDID. | |
--usb | off | Search USB devices only. | |
--wifi | off | Search Wi-Fi devices only. | |
--device-connection | both | Discovery: attached (USB), wireless (Wi-Fi), or both. Allowed: attached, wireless, both. | |
--route | Initial route the app navigates to on launch. | ||
--dart-entrypoint-args | -a | Pass arguments to the app main() (repeatable). | |
--verbose | -v | off | Verbose output. |
--udid wins over --device-id when both are set, and --usb / --wifi take precedence
over --device-connection. The run aborts before installing if the device reports an OS
older than iOS 17.
With the default both discovery and no explicit device selector, an attached USB device
is preferred over Wi-Fi. If no USB device is available, discovery falls back to wireless.
xcross flutter run
xcross flutter run -u 00008030-000123456789ABCD -D API_URL=https://staging.example.com
flutter build
Build a Flutter iOS .app without Xcode. The output is unsigned; signing happens when
xcross flutter run installs the bundle.
| Flag | Abbr | Default | Description |
|---|---|---|---|
--target | -t | lib/main.dart | The main entry-point file of the application. |
--flavor | Build a custom app flavor (sets FLUTTER_APP_FLAVOR). | ||
--dart-define | -D | Pass a KEY=VALUE define to the Dart compiler. Repeatable. | |
--dart-define-from-file | Load dart-defines from a .json or .env file. Repeatable. | ||
--[no-]pub | on | Run “flutter pub get” before building. | |
--build-name | Version name (CFBundleShortVersionString). | ||
--build-number | Version code (CFBundleVersion). | ||
--ipa | -i | off | Output a .ipa file instead of a .app. |
xcross flutter build --build-name 1.4.0 --build-number 42
xcross flutter build -i -t lib/main_staging.dart --flavor staging
flutter dap (internal)
ImportantThis command is hidden from xcross --help and is not intended to be run by hand. Your
IDE spawns it. Use xcross ide vscode or
xcross ide idea instead.
Debug Adapter Protocol server for IDE Run & Debug buttons. It takes no options and speaks DAP over stdio, so nothing else may write to stdout - xcross suppresses its own banner for this command. Running it in a normal terminal just leaves you with a process waiting for DAP frames.
VS Code reaches it through the generated .vscode/xcross_dap.dart shim; JetBrains IDEs
reach it through the LSP4IJ run configuration. Launch configurations must set
"xcross": true to be handled here.
ide vscode
Set up .vscode/ so F5, Restart and Hot Reload run on an iOS device.
xcross ide vscode
No options. It creates .vscode/, rewrites the xcross_dap.dart shim with the path of
the currently installed xcross, and upserts launch.json and settings.json in place -
existing keys are preserved, and a second run that changes nothing reports the files as
unchanged. Malformed JSON/JSONC is reported instead of overwritten.
ide idea
Set up a JetBrains DAP run config so Debug runs on an iOS device.
xcross ide idea
No options. It writes .run/xcross_ios_device.run.xml, a shared LSP4IJ
DAPConfiguration that starts xcross flutter dap over stdio with a launch
configuration marked "xcross": true. An existing file is never overwritten; the command
prints the XML for you to merge yourself.
completion
Print a shell completion script for xcross. Append the output to your shell config file (e.g. ~/.bashrc or ~/.zshrc) to enable tab-completion.
xcross completion >> ~/.bashrc
xcross completion >> ~/.zshrc
No options. The generated script covers bash and zsh (both compdef and compctl).
Restart your shell or source the file afterwards. Like flutter dap, this command owns
stdout, so the banner is suppressed - but unlike flutter dap it is a normal, visible
command meant to be run by you.