Skip to main content

xcodebuild Reference

A practical reference for xcodebuild and the Xcode command-line toolchain — building, testing, archiving, signing, simulator control, and more with real examples.

Xcode Toolchain Selection

Before you run any other command, make sure the shell is pointing at the right Xcode installation. xcode-select controls which toolchain every downstream tool (xcodebuild, xcrun, swift) will use.

CommandWhatWhen
xcode-select -pPrint the path to the active Xcode developer directoryConfirming which Xcode version the shell is currently using
xcode-select -s <path>Switch the active Xcode installationRunning CI with a specific Xcode version or testing a beta toolchain
xcode-select --installInstall the Xcode Command Line ToolsSetting up a fresh machine or CI runner without full Xcode
xcrun --sdk <sdk> --find <tool>Locate a specific tool within an SDKFinding the exact path to swift, clang, or another bundled tool
swift --versionPrint the Swift compiler versionConfirming the toolchain version before building or reporting issues
# Check active Xcode path
xcode-select -p
# /Applications/Xcode.app/Contents/Developer

# Switch to a beta installation
sudo xcode-select -s /Applications/Xcode-beta.app/Contents/Developer

# Install CLI tools on a fresh machine
xcode-select --install

# Find the swift binary in the active SDK
xcrun --sdk iphoneos --find swift

# Confirm Swift version
swift --version
# swift-driver version: 1.90 Apple Swift version 5.10

Workspace & Project Setup

With the toolchain confirmed, discover the structure of your project before running any build or test action.

Flag / CommandWhatWhen
xcodebuild -listList all schemes, targets, and configurations in the projectFirst thing to run when you’re unfamiliar with a project’s structure
-workspace <x.xcworkspace>Target a workspace file (CocoaPods, multi-project)Any project that uses a .xcworkspace — always prefer over -project in this case
-project <x.xcodeproj>Target a standalone Xcode project fileProjects that don’t use a workspace or CocoaPods
-scheme <name>Select which scheme to build or testRequired for workspace invocations; pick the app or framework scheme
-target <name>Select a specific target instead of a schemeUseful when building a single library target without running the full scheme
-configuration <Debug|Release>Choose the build configurationRelease for archives and distribution; Debug for day-to-day builds
# List everything available in the workspace
xcodebuild -list -workspace MyApp.xcworkspace

# List targets and configurations in a standalone project
xcodebuild -list -project MyLib.xcodeproj

Building

With a scheme selected, compile the app. You can do a full build, a test-only build, or a clean build to wipe stale artifacts.

CommandWhatWhen
xcodebuild buildCompile the app for the selected scheme and destinationVerifying the project compiles before running tests
xcodebuild build-for-testingBuild the test bundle without running testsCI pipelines that separate the build step from the test execution step
xcodebuild cleanRemove all build artifacts for the schemeForcing a clean state before an archive build or to fix stale build errors
xcodebuild clean buildClean then immediately buildOne command to guarantee a fresh compile
-destinationSpecify where to run the build (simulator, device, generic)Always required for build and test actions
-sdk <name>Explicitly set the SDK to build againstTargeting iphoneos for device builds or iphonesimulator for sim
# Build for an iPhone 16 simulator
xcodebuild build \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Debug \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest'

# Build for a generic iOS device (no specific device attached)
xcodebuild build \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Release \
  -destination 'generic/platform=iOS'

# Clean then build
xcodebuild clean build \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Debug \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest'

# Build test bundle only (no test execution)
xcodebuild build-for-testing \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest'

Testing

Once the code compiles, run your tests. You can run the full suite, split the build and run steps, or target a single test method.

CommandWhatWhen
xcodebuild testBuild and run all tests in the schemeRunning the full test suite locally or in CI
xcodebuild test-without-buildingRun previously built tests without recompilingCI: second step after build-for-testing, avoids redundant compiles
-only-testing:<target>/<class>/<method>Run a single test target, class, or methodDebugging a specific failing test without running the whole suite
-skip-testing:<target>/<class>Exclude a target, class, or method from the runTemporarily skipping a flaky test in CI while it is being fixed
-testPlan <name>Use a named .xctestplan fileRunning a specific test plan defined in Xcode (e.g. smoke vs. full)
-resultBundlePath <path>Write the full test results to an .xcresult bundlePreserving test results in CI for later analysis or reporting
-parallel-testing-enabled YESDistribute tests across multiple simulator clonesSpeeding up large test suites on machines with multiple cores
# Run all tests
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest'

# Run tests without rebuilding (after build-for-testing)
xcodebuild test-without-building \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest'

# Run only one test class
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
  -only-testing:MyAppTests/LoginTests

# Run only one specific test method
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
  -only-testing:MyAppTests/LoginTests/testValidCredentials

# Save results to a bundle for CI
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
  -resultBundlePath TestResults.xcresult

# Parallel testing with 4 simulator clones
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
  -parallel-testing-enabled YES \
  -parallel-testing-worker-count 4

Simulator Control (simctl)

Tests run on a simulator. Use simctl to boot, reset, and inspect simulators — either before a test run or to reproduce an issue by controlling the exact environment.

CommandWhatWhen
xcrun simctl listList all available simulators and their statesFinding the UDID or name of a simulator to use as a destination
xcrun simctl boot <udid>Boot a specific simulatorEnsuring a simulator is ready before running UI tests
xcrun simctl shutdown <udid>Shut down a running simulatorCleaning up after tests in CI
xcrun simctl erase <udid>Reset a simulator to factory stateGuaranteeing a clean state for reproducible test runs
xcrun simctl install <udid> <app>Install an .app bundle on a simulatorManually deploying a build for inspection or testing
xcrun simctl launch <udid> <bundle-id>Launch an installed app on a simulatorStarting the app programmatically, optionally with arguments
xcrun simctl push <udid> <bundle-id> <payload.json>Send a simulated push notificationTesting push notification handling without a real device or APNs
xcrun simctl io <udid> screenshot <file>Take a screenshot of a running simulatorCapturing UI state for debugging or visual regression baselines
xcrun simctl delete unavailableRemove all simulators for uninstalled Xcode runtimesReclaiming disk space after updating Xcode
# List all simulators
xcrun simctl list devices

# Boot a simulator by UDID
xcrun simctl boot "A1B2C3D4-..."

# Erase before a test run for a clean state
xcrun simctl erase "A1B2C3D4-..."

# Install and launch an app
xcrun simctl install booted ./build/MyApp.app
xcrun simctl launch booted com.example.MyApp

# Launch with arguments and environment variables
xcrun simctl launch booted com.example.MyApp \
  --args -UITesting \
  --env FEATURE_FLAG_LOGIN_V2=1

# Simulate a push notification
xcrun simctl push booted com.example.MyApp payload.json

# Screenshot
xcrun simctl io booted screenshot ./screenshots/home.png

# Clean up unavailable simulators
xcrun simctl delete unavailable

APNs Push Notification Payloads

Use xcrun simctl push booted <bundle-id> <payload.json> to fire any of these at a running simulator without a real device or APNs credentials. For physical devices, use curl against the APNs HTTP/2 API with a JWT or certificate.

Basic alert

// apns-alert.json
{
  "aps": {
    "alert": {
      "title": "New Message",
      "body": "Kyaw sent you a photo."
    },
    "sound": "default",
    "badge": 1
  }
}
xcrun simctl push booted com.example.MyApp apns-alert.json

Alert with subtitle and custom sound

// apns-subtitle.json
{
  "aps": {
    "alert": {
      "title": "Order Shipped",
      "subtitle": "Estimated delivery: tomorrow",
      "body": "Your order #4821 is on its way."
    },
    "sound": "chime.caf",
    "badge": 2
  }
}
xcrun simctl push booted com.example.MyApp apns-subtitle.json

Silent / background push

Background pushes wake your app without showing any visible notification. content-available: 1 is the only required key. alert, sound, and badge must be absent.

// apns-silent.json
{
  "aps": {
    "content-available": 1
  },
  "refresh": true,
  "payload_version": 2
}
xcrun simctl push booted com.example.MyApp apns-silent.json

Badge-only update

Update the app badge counter without showing an alert or playing a sound.

// apns-badge.json
{
  "aps": {
    "badge": 5
  }
}
xcrun simctl push booted com.example.MyApp apns-badge.json

# Clear the badge
echo '{"aps":{"badge":0}}' > apns-badge-clear.json
xcrun simctl push booted com.example.MyApp apns-badge-clear.json

Notification with action category

The category must match a UNNotificationCategory identifier registered in your app. Users see custom action buttons on long-press or in the notification centre.

// apns-category.json
{
  "aps": {
    "alert": {
      "title": "Friend Request",
      "body": "Alex wants to connect with you."
    },
    "sound": "default",
    "category": "FRIEND_REQUEST"
  },
  "sender_id": "user_9821",
  "sender_name": "Alex"
}
xcrun simctl push booted com.example.MyApp apns-category.json

Time-sensitive notification

Breaks through Focus modes (like Do Not Disturb). Requires the Time Sensitive Notifications entitlement in your app.

// apns-time-sensitive.json
{
  "aps": {
    "alert": {
      "title": "Verification Code",
      "body": "Your code is 482910. It expires in 5 minutes."
    },
    "sound": "default",
    "interruption-level": "time-sensitive"
  }
}
xcrun simctl push booted com.example.MyApp apns-time-sensitive.json

Critical alert

Plays a sound even when the device is muted or in Do Not Disturb. Requires an explicit entitlement granted by Apple.

// apns-critical.json
{
  "aps": {
    "alert": {
      "title": "Server Down",
      "body": "prod-api-01 is not responding."
    },
    "sound": {
      "name": "default",
      "critical": 1,
      "volume": 1.0
    },
    "interruption-level": "critical"
  }
}
xcrun simctl push booted com.example.MyApp apns-critical.json

Live Activity update

Updates a running Live Activity’s dynamic content. The content-state must match the ContentState type you defined in your ActivityAttributes conformance.

// apns-live-activity.json
{
  "aps": {
    "timestamp": 1718764800,
    "event": "update",
    "content-state": {
      "driverName": "Kyaw",
      "eta": "6 min",
      "status": "arriving"
    },
    "alert": {
      "title": "Almost there",
      "body": "Your driver is 6 minutes away."
    }
  }
}
# The bundle ID for a Live Activity push is the app's bundle ID
xcrun simctl push booted com.example.MyApp apns-live-activity.json

Sending to a real device via curl (APNs HTTP/2)

For physical devices you need a JWT (from your APNs Auth Key .p8 file) and the device token from didRegisterForRemoteNotificationsWithDeviceToken.

# --- generate JWT ---
TEAM_ID="ABCDE12345"
KEY_ID="FGHIJ67890"
KEY_FILE="./AuthKey_FGHIJ67890.p8"
BUNDLE_ID="com.example.MyApp"
DEVICE_TOKEN="abc123...64hexchars...def456"

# Build JWT header + payload (base64url encoded)
HEADER=$(echo -n '{"alg":"ES256","kid":"'"$KEY_ID"'"}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
NOW=$(date +%s)
PAYLOAD=$(echo -n '{"iss":"'"$TEAM_ID"'","iat":'"$NOW"'}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
SIGNING_INPUT="$HEADER.$PAYLOAD"
SIG=$(echo -n "$SIGNING_INPUT" | openssl dgst -sha256 -sign "$KEY_FILE" | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
JWT="$SIGNING_INPUT.$SIG"

# --- send the notification ---
curl --http2 \
  -H "apns-topic: $BUNDLE_ID" \
  -H "apns-push-type: alert" \
  -H "apns-priority: 10" \
  -H "authorization: bearer $JWT" \
  -d '{"aps":{"alert":{"title":"Hello","body":"Sent from curl"},"sound":"default"}}' \
  "https://api.push.apple.com/3/device/$DEVICE_TOKEN"
# apns-push-type values and when to use each:
#   alert          — standard visible notification
#   background     — silent push (content-available: 1)
#   voip           — VoIP call (requires PushKit entitlement)
#   complication   — watchOS complication update
#   fileprovider   — NSFileProviderManager change notification
#   mdm            — mobile device management command
#   location       — location push (iOS 15+)
#   liveactivity   — Live Activity create / update / end

Code Coverage

After a successful test run, measure what your tests actually exercise. Coverage reports tell you which lines and branches remain untested.

CommandWhatWhen
-enableCodeCoverage YESEnable code coverage data collection during testsGenerating coverage reports in CI or before a pull request
xcrun xccov view --reportPrint a human-readable coverage summary from an .xcresultReviewing overall coverage percentages after a test run
xcrun xccov view --file <source>Show coverage line-by-line for a specific source fileIdentifying exactly which lines or branches are not covered
xcrun xccov mergeMerge multiple .xcresult bundles into one reportCombining unit and UI test coverage into a single view
# Run tests with coverage enabled
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
  -enableCodeCoverage YES \
  -resultBundlePath TestResults.xcresult

# Print overall coverage report
xcrun xccov view --report TestResults.xcresult

# Coverage for a single file
xcrun xccov view --file MyApp/Sources/LoginViewModel.swift TestResults.xcresult

# Export as JSON (useful for third-party tools)
xcrun xccov view --report --json TestResults.xcresult > coverage.json

Build Settings & Output

Tune how the build runs and what it produces. These flags control compiler flags, warning policies, derived data location, and output verbosity.

FlagWhatWhen
-showBuildSettingsPrint all resolved build settings for the schemeDebugging which SDK, signing identity, or path Xcode is actually using
OTHER_SWIFT_FLAGS="-D FLAG"Inject a Swift compiler flag at the command lineEnabling a compile-time flag without modifying the project file
SWIFT_TREAT_WARNINGS_AS_ERRORS=YESFail the build on any Swift warningEnforcing a zero-warning policy in CI
-quietSuppress most build output; show only warnings and errorsCleaner CI logs when you only care about failures
-verbosePrint every build command as it runsDebugging build system issues or unexpected tool invocations
DERIVED_DATA_PATH=<path>Override the derived data directoryIsolating build artifacts per branch or keeping CI workspaces clean
# Print all resolved build settings
xcodebuild -showBuildSettings \
  -workspace MyApp.xcworkspace \
  -scheme MyApp

# Inject a compile-time flag
xcodebuild build \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'generic/platform=iOS' \
  OTHER_SWIFT_FLAGS="-DENABLE_EXPERIMENTS"

# Fail on warnings in CI
xcodebuild build \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'generic/platform=iOS' \
  SWIFT_TREAT_WARNINGS_AS_ERRORS=YES

# Clean CI derived data location
xcodebuild build \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'generic/platform=iOS' \
  DERIVED_DATA_PATH=./build/DerivedData

# Quiet output — only warnings and errors
xcodebuild build \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'generic/platform=iOS' \
  -quiet

Code Signing & Certificates

Before you can distribute a build, it must be signed. These commands inspect and verify signing identities, then handle the Apple notarization flow for macOS apps.

CommandWhatWhen
security find-identity -v -p codesigningList all valid code-signing identities in the keychainVerifying which certificates are installed and usable before a build
codesign -dv <app>Display the code signing details of an app bundleInspecting the signing identity and entitlements on a built artifact
codesign --verify <app>Verify that an app bundle is correctly signedChecking a build before submission to catch signing errors early
xcrun notarytool submitSubmit an app for Apple notarisationRequired before distributing a macOS app outside the App Store
xcrun notarytool waitWait for a notarisation request to completeBlocking a CI step until Apple’s notary service finishes
xcrun stapler stapleAttach the notarisation ticket to the app bundleFinal step before distributing a notarised macOS app
# List signing identities
security find-identity -v -p codesigning
# 1) ABC123... "Apple Distribution: My Company (TEAM123)"
# 2) DEF456... "Apple Development: kyaw@example.com (TEAM123)"

# Inspect signing on a built app
codesign -dv --verbose=4 ./build/MyApp.app

# Verify signing
codesign --verify --deep --strict ./build/MyApp.app

# Notarise a macOS app (stored credentials)
xcrun notarytool submit MyApp.zip \
  --keychain-profile "notarytool-profile" \
  --wait

# Staple the ticket once notarisation is approved
xcrun stapler staple MyApp.app

Archiving & Exporting

The final step: produce a release-ready archive and export it as a distributable .ipa or .app for App Store Connect, TestFlight, or ad-hoc distribution.

CommandWhatWhen
xcodebuild archiveBuild a release .xcarchivePreparing a build for App Store submission or ad-hoc distribution
-archivePath <path>Set where the .xcarchive is writtenAlways specify to control output location in CI
xcodebuild -exportArchiveExport an .ipa from an existing .xcarchiveConverting an archive to a distributable .ipa after signing
-exportOptionsPlist <path>Provide export options (method, signing identity, provisioning)Required for -exportArchive; defines App Store vs ad-hoc vs enterprise
-exportPath <path>Set the output directory for the exported .ipaControlling where CI places the final distributable artifact
# Step 1 — archive
xcodebuild archive \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  -archivePath ./build/MyApp.xcarchive

# Step 2 — export .ipa
xcodebuild -exportArchive \
  -archivePath ./build/MyApp.xcarchive \
  -exportOptionsPlist ExportOptions.plist \
  -exportPath ./build/ipa/
<!-- ExportOptions.plist (App Store upload) -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>method</key>
  <string>app-store-connect</string>
  <key>teamID</key>
  <string>ABCDE12345</string>
  <key>uploadSymbols</key>
  <true/>
  <key>compileBitcode</key>
  <false/>
</dict>
</plist>