Troupeby AliveDocsSign in

Platforms

iOS Simulator

Troupe runs your app on iOS Simulators that it creates, boots and deletes itself. This page covers what your Mac needs, how Troupe drives a simulator, and what the platform can and cannot do.

The platform's id is ios-simulator. A troupe.config.ts may write ios.

Requirements

  • macOS on Apple silicon. Intel Macs are not supported.
  • A full Xcode, not only the Command Line Tools, with at least one iOS simulator runtime. Xcode 27.1 and 27.2 with the iOS 27.0 runtime are verified. Xcode 26 is not verified yet.
  • Bun 1.4.3 or newer.
  • Memory for each simulator: Troupe budgets 3,194 MB for a session and starts one only when that fits.

Prepare the platform

Run this once in your project:

Shell
bunx troupe install ios

It records your Xcode in ~/.troupe/host.json (the one DEVELOPER_DIR names, else /Applications/Xcode.app/Contents/Developer), runs the environment checks and prints a fix for each failure, builds Troupe's XCUITest runner and a simulator template, and starts one simulator to prove the path. Install describes the checks.

A project then names the device and the app:

TypeScript
import { defineConfig } from '@alivelabs/troupe'

export default defineConfig({
  projects: [
    {
      name: 'ios',
      use: { platform: 'ios', deviceType: 'iPhone 18 Pro', os: '27.0', app: './build/Shop.app' },
    },
  ],
})

How Troupe drives a simulator

Three parts do the work. Troupe owns all three: it does not depend on Appium, WebDriverAgent or another driver.

PartWhat it does
simctlCreates, boots and deletes the simulator; installs and launches apps; sets permissions, location and appearance; follows the logs
The XCUITest runnerReads the accessibility tree, resolves locators and performs test actions. Troupe builds it on your Mac with your Xcode and keeps it in $TROUPE_HOME/cache/ios-runner/
simcapCaptures the screen as H.264 for the live stream and delivers live input from a viewer. It ships with the platform package

Test actions and live input take different paths. A test's tap() or fill() goes through the XCUITest runner. A click in Troupe Studio goes through simcap. The capabilities below list both.

Devices are Troupe's own

Troupe creates simulators named troupe-… and acts only on those, by UDID. It never boots, erases or deletes a simulator you created.

A session's simulator is a clone of a template that Troupe booted once (troupe-tpl-…), so it boots without the data migration a new simulator runs on its first boot. The template never runs a session, and a clone is deleted when its device shuts down, so a new device has no app, setting or keychain item from an earlier one. troupe install ios builds the template ahead of time.

A released device stays warm for a few minutes and may serve the next session with the same spec, with its apps still installed. See Release.

Device Hub and Simulator.app

Quit Xcode's Device Hub and Simulator.app while Troupe runs devices. Either can attach to Troupe's simulators, and live input may then be unavailable: the stream says so with the reason device-hub-attached, and Troupe Studio shows Input unavailable. troupe doctor reports them as the ios.deviceHub check (ENVIRONMENT_DEVICE_HUB_ACTIVE). By default that check warns; test actions are not affected.

Apps

Troupe installs iOS Simulator builds:

  • a .app directory, such as Xcode's Debug-iphonesimulator/Shop.app;
  • an archive that holds one .app: .ipa, .zip, .tar.gz or .tgz.

A build for a physical device is refused with APP_INSTALL_FAILED and the reason wrong-platform. Real devices are not supported.

Launch arguments, launch environment variables, clearing an app's data and deep links all work. A deep link's scheme is approved ahead, so iOS shows no "Open in" prompt.

Capabilities

The runtime reports these through runtime.capabilities, and refuses a call the platform cannot serve with CAPABILITY_UNSUPPORTED before it touches the device.

AreaiOS Simulator
LocatorsRole, text, label, placeholder and test id, with regular expressions; system UI and alerts through screen.system
Test idaccessibilityIdentifier (React Native's testID)
Touch in testsOne contact: tap, double tap, long press, swipe, drag, scroll
Keyboard in testsText. There are no key events
Hardware buttons in testshome
RotationYes
Permissionscalendar, contacts, location, locationAlways, microphone, motion, photos, reminders
System settingsLocation, appearance and the clipboard
ScreenshotsYes
Live streamH.264, with frame rate and bitrate changed while streaming
Live inputUp to five contacts, keys and text, the buttons home, power, volumeUp and volumeDown, rotation
LogsSystem log, app log and crash reports
Screen recordingNo

Limits

  • Test actions use one finger. input.pinch and an input.gesture with more than one contact are refused.
  • input.pressKey needs key events, so screen.pressKey() is refused. Type text with fill() or type() instead.
  • input.hideKeyboard is not available.
  • The permissions camera and notifications cannot be set: simctl has no service for them.
  • Live text is typed key by key on a US keyboard layout. Text with a character that layout cannot type is refused.
  • Live rotation is offered once the session is ready.

Device types and OS versions

The device types and OS versions are those your Xcode has. Troupe reads them from simctl, so a device type is offered only for a runtime that hosts it.

  • deviceType is a simulator device type by name ('iPhone 18 Pro') or by its identifier. Without one, Troupe takes the newest numbered iPhone Pro that an installed runtime hosts.
  • os is an iOS version such as '27.0'. Without one, or with 'latest', Troupe takes the newest installed runtime that runs the device type.

List what a runtime offers with catalog.deviceTypes and catalog.osVersions:

TypeScript
const { deviceTypes } = await troupe.call('catalog.deviceTypes', { platform: 'ios-simulator' })
const { osVersions } = await troupe.call('catalog.osVersions', { platform: 'ios-simulator' })

A version that is not installed fails with ENVIRONMENT_IOS_RUNTIME_MISSING. Its fix is the xcodebuild command that downloads the runtime. An unknown device type is DEVICE_SPEC_INVALID, with the available names in its data.

A spec may also set locale, timeZone, orientation and appearance. See the device spec.

Host settings

troupe install ios writes platforms.ios-simulator in $TROUPE_HOME/host.json. You can edit these keys:

KeyDefaultWhat it does
developerDir/Applications/Xcode.app/Contents/DeveloperThe Xcode Troupe uses. The daemon reads only this, never your shell's DEVELOPER_DIR
maxDevices2The most simulators running at once
enabledtruefalse switches the platform off: a request for it fails with ENVIRONMENT_PLATFORM_DISABLED
templatestruefalse creates every simulator from scratch instead of cloning a template
deviceHubwarnrefuse stops Troupe from booting a simulator while Device Hub or Simulator.app runs
runnerReadyTimeoutMs180000How long the XCUITest runner may take to start

Next

esc

    Type to search every page, the protocol reference and the cloud API.