Before you start
This guide assumes:
FlowDeck is installed and your trial is active. If not, follow the getting-started guide first.
You have at least one existing
xcodebuildscript you'd like to migrate. Afastlanelane that shells out toxcodebuildworks too.The script currently runs (even if grudgingly).
By the end you'll have a side-by-side translation of your existing commands, a smoke-tested replacement, and a switch-over plan that keeps xcodebuild available as a fallback the whole time.
Audit your current xcodebuild commands
List every place your project shells out to xcodebuild, xcrun simctl, xcrun devicectl, or log stream. Common locations:
A
scripts/ortools/directory in your repo.Your CI config (
.github/workflows/,bitrise.yml,Jenkinsfile, etc.).A
Fastfilewithsh "xcodebuild ..."calls.Build phases inside Xcode that shell out.
Makefiles,
justfiles, ornpmscripts in mixed-repo projects.
For each call site, write down what it does in plain English. "Build for CI release on iPhone 16 simulator." "Run the smoke test suite." "Boot a simulator and wait for it." The plain-English version is your migration target.
Translate command by command
Use this table as a starting point. The patterns are deliberately mechanical so the migration can happen one line at a time.
What you're doing |
| FlowDeck |
|---|---|---|
Discover schemes |
|
|
Build for a simulator |
|
|
Run a single test |
|
|
Skip a slow test |
|
|
Boot a simulator |
|
|
Stream logs from one app |
|
|
Clean derived data |
|
|
Parse output | Pipe through a regex, hope subsystem strings didn't change | Add |
This is not a one-to-one feature map. xcodebuild archive, xcodebuild -exportArchive, and the signing-and-distribution pipeline don't have FlowDeck equivalents, those operations are out of scope. Keep xcodebuild for them.
Run both in parallel and compare
Before switching anything, run the FlowDeck command alongside the existing xcodebuild call. This catches surprises while you still have a rollback.
Compare the exit code, the time taken, and the failure mode if anything goes wrong. The FlowDeck command should be at least as fast (often faster, because scheme discovery is native instead of a xcodebuild -list hit) and the exit code should match.
If output differs (FlowDeck reports a warning your CI ignored, or your CI flags an error FlowDeck rolled up), it's almost always FlowDeck being more precise. Investigate before assuming it's wrong.
Save a project config to eliminate flag repetition
One of the wins of migrating is that you stop passing the same -workspace / -scheme / -destination on every command. FlowDeck reads from a per-project config:
After that, flowdeck build, flowdeck run, flowdeck test, and the rest pick up the saved values. The config is stored in .flowdeck/ in your project root. CI commits it (so jobs use the same configuration); developers either commit it or .gitignore it depending on whether your team standardizes on one scheme.
You can still pass flags on the command line; they override the saved config for that single call. Useful for "build the same scheme but on a different simulator just this once."
Switch over piecemeal
The lowest-risk migration is one job at a time. A typical order:
Local developer scripts first. The
build-and-run.shsomeone wrote three years ago. Lowest blast radius, easy to roll back.PR validation builds next. The fastest, most-run CI job. Catches problems quickly because it runs on every push.
Nightly regression runs after that. Once PR jobs are stable, move the nightly suite. Volume is lower; observation period is longer.
Release builds last. Or not at all. If your release job uses
xcodebuild archiveand-exportArchive, leave it onxcodebuild. Releases are not the place to swap out tooling.
Keep the previous script in version control with a date in the filename (build.legacy.sh, workflow-2026-03.yml) until you've shipped through a full release cycle on the new tooling. The cost of keeping it is small. The cost of needing it and not having it is large.
If something breaks, roll back fast
The migration is designed so any single job can revert to raw xcodebuild without disturbing the rest. Concrete recipe:
Revert the one file. If a PR validation job fails after the swap, restore the
build.legacy.sh(orworkflow-2026-03.yml) you kept around. Jobs are independent; the local dev script and the nightly job keep using FlowDeck.Capture the diff. Before reverting, save the FlowDeck output:
flowdeck build --json > /tmp/failed-build.ndjson. The structured events make it obvious where FlowDeck andxcodebuilddiverged. Almost always a different scheme, configuration, or simulator selection, not a real build difference.File an issue with the NDJSON attached. FlowDeck issues with structured output attached are resolved faster than "the build failed."
Once the migrated job is back on FlowDeck and stable for a release cycle (PRs, nightly, release), the legacy script can be deleted. Not before.
Convert text parsers to JSON consumers
If your CI has a script that parses xcodebuild output to detect failures or count warnings, replace it. The new version is shorter and won't break across Xcode versions:
The same pattern works for tests:
And for build summaries you can dump to a CI artifact:
Stable schemas mean these parsers don't break the next time Apple changes a string in xcodebuild's output.
Patterns that come up during migration
Keep xcodebuild for archive and export.
FlowDeck doesn't wrap xcodebuild archive or -exportArchive. Release pipelines that produce a signed .ipa should stay on raw xcodebuild. There's no benefit to migrating them.
Use --verbose when debugging a migration.
FlowDeck summarizes output by default. If you're comparing against a xcodebuild baseline and the FlowDeck output looks suspiciously short, pass --verbose to see the underlying xcodebuild stream. Useful for catching cases where your old script depended on a warning the summary suppressed.
Compose with fastlane, don't replace it.
If you have a Fastfile that orchestrates lanes, the right move is usually to keep the Fastfile and swap the sh "xcodebuild ..." calls inside lanes for sh "flowdeck ...". Fastlane keeps its orchestration role; FlowDeck replaces the manual xcodebuild plumbing.
Pin the FlowDeck version in CI.
Same way you pin Xcode in CI. The install script accepts a --version argument, so you can pin to a known build for reproducibility: curl -sSL https://flowdeck.studio/install.sh | sh -s -- --version <tag>.
When things go wrong
FlowDeck “succeeds” but produces no output where xcodebuild did
The build is slower than expected on CI
A test that passed in xcodebuild fails in FlowDeck (or vice versa)
Some xcodebuild flag isn’t supported
The signing setup breaks under FlowDeck
