skip to content

In Appium, what does the XCUITest driver do with xcodebuild before an iOS session runs?

level: middleimportance: must knowfreq 62%

answer

  1. the session starts with a compile
  2. xcodebuild runs on the host
  3. sign, install, launch, then poll
  4. derived data makes later runs cheaper

basics

~20 s

Before the first command, Appium's XCUITest driver compiles WebDriverAgent with xcodebuild on the macOS host, signs it, installs it on the simulator or device, launches it under XCTest, and waits for its HTTP server to answer.

solid answer

~40 s

The XCUITest driver ships no ready-made agent for your target, so its default startup strategy is to build one. It drives `xcodebuild` on the macOS host against the WebDriverAgent project, signs the resulting runner — mandatory on a real iPhone, where the OS will not launch a runner it cannot verify — installs it with `simctl` for a simulator or `devicectl` for a real device, launches it under XCTest and polls its HTTP server until it answers on `appium:wdaLocalPort`. Only then does your first command go through. Build products land in a derived-data directory that `appium:derivedDataPath` can name, which is why the second session on a machine is usually far cheaper than the first.

go deeper

for a junior

Know that an Appium iOS session starts by building and installing WebDriverAgent with xcodebuild, and that this is why the first session on a machine is slow. Naming the steps in order is enough here.

for a middle

Walk the sequence: build, sign, install, launch, poll, then hand over. Say where the products go, why appium:derivedDataPath matters, and which step differs between a simulator and a real iPhone.

for a senior

Show you treat the build as infrastructure — Xcode on every host, a signing identity per device, derived data as state that upgrades invalidate, and the schedule impact of a compile inside every session.

for a principal

Frame it as a coupling decision: the default strategy puts a compiler and a signing identity in the critical path of every run, and the alternatives move that cost onto an artifact your organisation must own.

## The default is a build, not a connection Appium's XCUITest driver does not ship a ready-made agent for your target. Its default startup strategy is to make one: it drives `xcodebuild` on the macOS host against the WebDriverAgent project, then puts the result on the simulator or device. Your app takes no part in that build — for the worked example here, a hotel housekeeping-status app whose room list a suite taps through, nothing in its source is compiled by this step. WebDriverAgent is a separate XCTest runner, installed and launched independently of the app under test. That single fact explains most of what feels strange about an Apple-platform session start. A browser session connects to a driver process; an Appium XCUITest session compiles a piece of software first. ## The sequence, step by step 1. **Build.** `xcodebuild` compiles the WebDriverAgent runner on the host. This is the expensive step, and the reason a first session on a fresh machine can take dramatically longer than the tenth. 2. **Sign.** The runner is code-signed. On a real iPhone this is not optional — the operating system will not launch a test runner it cannot verify — and it is what the driver's signing capabilities, such as `appium:xcodeOrgId`, exist to feed. A simulator does not impose the same provisioning requirement. 3. **Install.** The signed runner is installed on the target: `simctl` for a simulator, `devicectl` for a real device. 4. **Launch and poll.** The runner starts under XCTest and the driver waits for its HTTP server to answer on the host-side port named by `appium:wdaLocalPort`. 5. **Hand over.** Only once the agent answers does the driver let the session's first W3C command through. Steps one to four all happen before your test has done anything at all. The session-start latency you measure is mostly them. ## Where the build products live The compiled output lands in a derived-data directory, which `appium:derivedDataPath` can point at explicitly. That directory is why a second session is usually cheaper than the first: when the products there are still valid for the driver and target in play, there is far less to compile. It is also state. A build cache is an asset your lane owns — it takes disk, an Xcode or driver upgrade can invalidate it, and two lanes pointed at the same directory on a shared machine are less isolated than they look. ## What changes with the target | | iOS simulator | Real iPhone | |---|---|---| | signing | no device-trust requirement | must be signed with an identity the device trusts | | install path | `simctl` | `devicectl` | | host for the default strategy | macOS with Xcode, which is also where simulators run | macOS with Xcode | | reaching the agent | server on the host itself | reached across the device link | Naming the target kind matters because `simctl`, `devicectl` and `xcodebuild` are Apple-side tools. None of them appears in an Android UiAutomator2 session, which pushes and starts the prebuilt `io.appium.uiautomator2.server` helper instead of compiling anything. ## What the build couples your lane to - **A macOS host with Xcode.** The default `xcodebuild` startup strategy has nowhere else to run. - **A signing identity**, for every real device the lane touches. - **Time**, on every session that cannot reuse a valid build. - **The driver version.** The agent comes from the driver's own expectations, so upgrading the driver generally means the agent is rebuilt rather than reused. - **Disk**, for derived data that nothing prunes for you. ## Turning it off is a decision, not a fix Two capabilities opt out of parts of this. `appium:usePreinstalledWDA` tells the driver to use an agent already installed on the target instead of building one; `appium:webDriverAgentUrl` points it at an agent already running, so build, install and launch are all skipped. Both trade session-start time for an artifact somebody now maintains, which makes them a lane-level decision rather than a per-session tweak. ## The mental model to keep Treat `xcodebuild` here as part of the session, not part of your build pipeline. The XCUITest driver is, at startup, a build tool with a WebDriver interface attached: before it can answer a single find for the housekeeping app's Room 412 row, it has compiled, signed, installed and started a second application on the target. Everything you later tune about Apple-platform session start — caching, prebuilt agents, ports, reuse — is a way of doing less of that sequence, less often.

  • Why is the first XCUITest session on a fresh machine so much slower than the tenth?
    Because the first one actually compiles WebDriverAgent. Later sessions reuse the build products in the derived-data directory, which `appium:derivedDataPath` can name, when those products are still valid for the driver and target in play — installing and launching a runner is far cheaper than building one.
  • What differs in this sequence between a simulator target and a real iPhone?
    Mostly signing and the install path. A real device needs the runner signed with an identity it trusts and installed via `devicectl`; a simulator install goes through `simctl` and imposes no comparable provisioning requirement. The build and launch steps themselves have the same shape.

saying these in an interview costs you the question

  • Thinking xcodebuild compiles the app under test rather than the agent
  • Assuming a simulator session skips the agent build entirely
  • Treating the agent build as one-off setup rather than a session step
  • Saying code signing only matters when shipping an app to users
  • Expecting a non-macOS host to run the default xcodebuild startup strategy