IOSDriver drives iOS apps through the XCUITest engine. The setup process is more involved than Android — you deal with code signing, simulator vs real device differences, and WebDriverAgent compilation — but the resulting driver is stable and fast.
XCUITestOptions
XCUITestOptions is the typed capability builder for iOS sessions, analogous to UiAutomator2Options for Android:
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.options.XCUITestOptions;
import java.net.URL;
URL serverUrl = new URL("http://127.0.0.1:4723");
XCUITestOptions options = new XCUITestOptions()
.setDeviceName("iPhone 15")
.setPlatformVersion("17.0")
.setApp("/path/to/MyApp.app") // simulator build (.app)
.setSimulatorStartupTimeout(Duration.ofSeconds(120))
.setWdaStartupRetries(3);
IOSDriver driver = new IOSDriver(serverUrl, options);Simulator vs real device
The setApp() path differs by target:
// Simulator — use .app bundle (unzipped)
options.setApp("/Users/me/builds/MyApp.app");
// Real device — use .ipa file (signed)
options.setApp("/Users/me/builds/MyApp.ipa");For real devices, you also need:
options.setUdid("00008030-001A23456789002E") // device UDID from `xcrun xctrace list devices`
.setXcodeCertificate("Apple Distribution: Company Name (TEAMID)")
.setXcodeOrgId("TEAMID");Simulator management
Appium boots a simulator if the named device isn't running. To control which simulator is used:
options.setDeviceName("iPhone 15 Pro")
.setPlatformVersion("17.2");List available simulators with xcrun simctl list devices available. The device name must match exactly, including any Pro/Max/Plus suffixes.
To reuse a running simulator instead of booting a fresh one:
options.setSimulatorStartupTimeout(Duration.ofSeconds(60))
.setWdaLocalPort(8100) // avoid port collision in parallel runsPreventing simulator shutdown between sessions
Booting a simulator takes 30–90 seconds. setKeepAlive(true) tells the XCUITest driver to leave the simulator running after the session ends:
XCUITestOptions options = new XCUITestOptions()
.setDeviceName("iPhone 15")
.setKeepAlive(true);The next session on the same simulator name reuses the running instance, shaving a minute off suite time.
App reset on iOS
// No reset — fastest, use for stateless tests
options.setNoReset(true);
// Default — app data cleared, app stays installed
options.setNoReset(false);
// Full reset — app uninstalled completely
options.setFullReset(true);iOS full reset uninstalls the app and its data container. Use this when tests depend on a completely fresh install (e.g., onboarding flows, permission grant flows).
IOSDriver-specific methods
IOSDriver driver = new IOSDriver(serverUrl, options);
// Simulator-only: shake gesture
driver.shake();
// Get current bundle ID
String bundleId = driver.getBundleId();
// Activate an app by bundle ID
driver.activateApp("com.example.myapp");
// Terminate
driver.terminateApp("com.example.myapp");
// Lock / unlock device (real device only)
driver.lockDevice();
driver.unlockDevice();
// Read clipboard
String text = driver.getClipboardText();System alerts
iOS shows permission dialogs (camera, location, notifications) as native system alerts outside the app's hierarchy. Handle them with:
try {
driver.switchTo().alert().accept(); // "Allow"
} catch (NoAlertPresentException e) {
// no alert shown — that's fine
}Or use the XCUITest capability to accept all permissions automatically:
options.setAutoAcceptAlerts(true);Be careful with setAutoAcceptAlerts(true) if any test verifies the permission denial path — it accepts every dialog regardless.
WebDriverAgent troubleshooting
WebDriverAgent (WDA) is the XCTest runner Appium compiles and installs on the target device/simulator. Session creation fails if WDA doesn't start. Common causes:
WDA timeout: Increase the startup wait:
options.setWdaStartupRetries(3)
.setWdaStartupRetryInterval(Duration.ofSeconds(20));Port conflict in parallel runs: Each simulator session needs its own WDA port:
// Session 1
options.setWdaLocalPort(8100);
// Session 2
options.setWdaLocalPort(8101);Code signing error on real device: Xcode must trust the development certificate. Set xcodeOrgId to the 10-character team ID from your Apple Developer account.
Parallel iOS sessions
Running iPhone and iPad tests in parallel:
// Thread 1 — iPhone
XCUITestOptions iphone = new XCUITestOptions()
.setDeviceName("iPhone 15")
.setWdaLocalPort(8100)
.setWebDriverAgentUrl("http://127.0.0.1:8100");
// Thread 2 — iPad
XCUITestOptions ipad = new XCUITestOptions()
.setDeviceName("iPad Pro (12.9-inch) (6th generation)")
.setWdaLocalPort(8101)
.setWebDriverAgentUrl("http://127.0.0.1:8101");Without distinct wdaLocalPort values, both sessions compete for port 8100 and one fails.