Skip to main content
runcloud xcode build uploads a bounded snapshot of your source, builds it on macOS, streams a sanitized xcodebuild log, and keeps the build record and artifact available through the authenticated API. You do not need an active simulator session.

Build a simulator app

Authenticate once, then run the command from a directory that contains an Xcode project or workspace:
The default SDK is iphonesimulator. Native projects default to Debug; detected React Native and Expo projects default to Release. If --scheme is omitted, run.cloud uses the first shared scheme reported by the selected workspace or project. The output for an iOS simulator build is a gzip-compressed .app bundle. An unsigned iphoneos build returns an app bundle, signed iOS builds return an IPA, and watchOS builds return a compressed watch app.

Observe a detached build

Every accepted build has a durable ID. A detached client can reconnect, inspect state, resume reading the sanitized log, download an artifact, or cancel work:
Lifecycle states are uploading, queued, running, succeeded, failed, and cancelled. Terminal records include an exit code. Failed records also include the stage, a specific reason extracted from the retained log, a failure category, and a suggested next action. The artifact endpoint requires the same organization access as the build; any storage redirect it returns is short lived. Use --idempotency-key in CI. Repeating the same key with the same source and options returns the original build. Reusing it with different input fails with 409 instead of starting ambiguous duplicate work.

Reuse the output as an Asset

Every verified build output has an immutable, organization-scoped Asset ID in artifact.assetId. The same output can be installed into several sessions without downloading and uploading it again:
mobile launch is one retry-safe operation: it selects the output, creates a session, installs the app, and returns the launch ID, session ID, current state, or retained failure. Repeating the same key and request returns the same operation and never allocates a second simulator. A changed request with the same key returns 409. The Asset ID is the authorization boundary, not a storage URL. run.cloud checks that the build output and target session belong to the same organization, then creates a short-lived, read-only download for the selected simulator host. The Mac that built the app and the host running the simulator do not exchange credentials and do not presign for each other.

Rebuild only JavaScript

An Expo simulator build records a fingerprint of its native shell. When only JavaScript and bundled assets change, reuse that shell:
Rebundles are Release iphonesimulator outputs. run.cloud installs dependencies, calculates the current native fingerprint, and stops with a compatibility failure when native inputs differ from the basis. That failure includes the action to run a full build and use the new build as the next basis. The original Asset is never modified; the rebundle creates a new Asset whose parentAssetId identifies its native-shell basis.

Replay build events

Organization event sequences are ordered and replayable. Save nextCursor only after processing the returned page:
Use --build-id to select one build. Events contain product-visible state, stage, output Asset metadata, and sanitized failure details without requiring access to the build worker.

Delegate one launch safely

An organization member can mint an opaque capability for a worker that should read and launch one output, without sharing the organization API key:
Capability tokens expire after 60 to 3,600 seconds, can be revoked sooner, and are bound to one organization, explicit resource IDs or idempotency keys, and named scopes. The secret is returned only by capability create; list commands show metadata, never the token.

Project preparation

The builder supports these source preparation modes:
  • Existing .xcworkspace or .xcodeproj: select one with --workspace or --project. They are mutually exclusive.
  • XcodeGen: pass --xcodegen-spec, plus optional --xcodegen-project and --xcodegen-project-root paths.
  • React Native or Expo: package installation and native project generation are detected from package.json; select a monorepo app with --expo-app-dir.
  • Git-dependent generation: pass --git-init to initialize the uploaded snapshot before project preparation.
For a Debug React Native build, --dev-server-url preserves the launch URL in the build request. Because this service does not pair a simulator, it does not open the URL automatically. It is rejected for Release builds. Dependency preparation recognizes npm, pnpm, Yarn, CocoaPods, Swift Package Manager through Xcode, and Carthage project files. Source packaging omits VCS data and common Xcode/dependency output directories. The root .gitignore is honored, while .xcconfig files are preserved. Use repeatable source controls when the defaults do not fit:
--ignore and --include accept regular expressions. Additional destinations must stay inside the uploaded workspace or ~/; symbolic links are rejected. The compressed source limit is 100 MiB.

Build settings

Use a repeatable --build-setting KEY=VALUE for SWIFT_ACTIVE_COMPILATION_CONDITIONS or an APP_CONFIG_ key:
Values for APP_CONFIG_ keys are stored and logged as redacted secret inputs. Arbitrary Xcode settings are rejected so a request cannot alter the runner or signing environment.

Signing and App Store upload

For manual signing, provide one PKCS#12 certificate and one or more provisioning profiles. Device builds are selected automatically when signing is configured:
Cloud-managed signing accepts app-store-connect, release-testing, or debugging as its export method:
--auto-build-number finds the highest numeric build number for the app in App Store Connect and uses the next value. An App Store upload happens after the IPA is retained by run.cloud, so a failed upload can still leave a downloadable artifact. Certificate material, private keys, passwords, profiles, and signed upload URLs are not returned in build records. Use --upload <filename> to choose the retained artifact filename, or --signed-upload-url <https-url> to also upload the output to your own short-lived destination. Those options are mutually exclusive.

TypeScript

The public SDK packages the same source snapshot and follows the same lifecycle:
For a JavaScript-only change, call cloud.xcode.rebundle(process.cwd(), completed.id, options). Consume ordered events with cloud.xcode.events({ after: cursor }) and persist the returned nextCursor after handling the page. See the TypeScript SDK reference for all option and response types.

Intentional differences

The service intentionally keeps the following boundaries around remote builds:
  • Each request uses an immutable uploaded snapshot. It does not expose a persistent Mac instance, incremental source sync, DerivedData reuse, or a customer-managed cache toggle.
  • Native-shell reuse is limited to compatible Expo Release simulator builds. Native changes require a full build; device signing and App Store uploads do not use the rebundle path.
  • A successful build remains independent of simulator capacity. Use the explicit, idempotent mobile launch operation when a simulator should be created and the output installed.
  • --no-wait and --detach use the durable build ID for reconnection. There is no per-build callback URL option. Consumers replay ordered organization events instead of registering an outbound webhook.
  • Live command, stdout, and stderr events are combined into one ordered, sanitized log that can be resumed by byte offset.
  • Every verified output is an immutable organization Asset. --upload chooses its filename; it does not return a durable bearer download URL or make a build output independently deletable.
  • Source symlinks are rejected instead of dereferenced or recreated.
  • --asc-wait-timeout is accepted for command compatibility, but run.cloud reports successful upload acceptance and leaves App Store processing asynchronous.
These differences do not change the terminal Xcode result: build state, logs, artifact metadata, output bytes, and actionable failures remain available outside the client that submitted the request.