Physical devices
Lease, drive and test on real Android phones and iPhones with warden claim, warden ui and warden e2e, and keep it off devices that are not yours.
warden leases real phones the same way it leases simulators, but only when you ask: --physical, or naming the device with --serial / --udid. A plain warden claim never takes a phone.
warden devices --physical # what warden sees, and why a device is skipped
warden claim android --physical --json # a connected, authorized phone
warden claim ios --udid 00008140-000A0B0C0D0E0F10 --json
warden ui snapshot -i --udid 00008140-000A0B0C0D0E0F10
warden e2e smoke --physical --platform ios
warden ui close --releaseA physical device is never booted, created, shut down, erased or slimmed. It costs nothing in host capacity, and warden release --shutdown and warden gc leave it running.
Claim
| Command | Takes |
|---|---|
warden claim android --physical | one phone whose adb state is device |
warden claim ios --physical | one iPhone or iPad that is paired, has Developer Mode on and is reachable now |
warden claim android --serial <serial…> | exactly these phones (adb serial, or ip:port for wireless debugging) |
warden claim ios --udid <udid…> | exactly these devices (CoreDevice UDID) |
--physical is refused together with --count, --max, --slim, --headless, --runtime and --adopt.
Only reachable devices are picked
devicectl lists every device an Apple ID has ever paired, including ones in a drawer, as available. warden counts an iPhone or iPad as ready only when it answers: its tunnel is connected, it is on a cable, or a bounded devicectl device info lockState call returns within 5 seconds. Anything else is listed with a reason and never picked automatically:
PLATFORM KIND NAME ID STATE
ios physical Test iPad 00008132-000A0B0C0D0E0F11 offline
Test iPad (00008132-000A0B0C0D0E0F11): offline: The device is not connectedNaming an unreachable device with --udid waits up to --wait (default 30m, with waiting for … lines about every 30 s) and then fails with device_offline and the reason; --no-wait fails at once. A locked iPhone is waited for up to 2 minutes (<name> is locked: unlock it to continue), then fails device_locked (exit 2). An Android phone in adb state unauthorized or offline is likewise listed with a hint (accept the USB debugging prompt, reconnect the cable) and never picked.
Restrict which devices warden may take
If a family member's iPad or a colleague's phone shares your machine, give --physical a list. Without ids, it then picks only from the list:
export default defineConfig({
physical: { allow: ["00008140-000A0B0C0D0E0F10", "R5CT123ABC"] },
// …
});export WARDEN_PHYSICAL_IDS=00008140-000A0B0C0D0E0F10,R5CT123ABC # wins over the config- The list applies to
warden claim,warden ui --claim --physicalandwarden e2e|run|batch --physical. - Naming a device with
--udid/--serialalways works: an explicit request beats the list. - A list that is set but matches nothing connected makes the claim wait and say so.
- The setting lives in the repo config and the environment. There is no machine-wide config file: put the variable in your shell profile to cover every repo.
Drive with warden ui
warden ui works on a leased phone like on a simulator: snapshot, press, fill, type, swipe, screenshot, open, wait. Single-finger input only on an iPhone: pinch, rotate, gesture, press shake and press back fail E_UNSUPPORTED.
warden ui open --claim --physical --platform android
warden ui snapshot -i
warden ui close --releaseA locked phone exits 2 with device_locked; warden never unlocks one. Rotation, dark mode and animation scales on Android are saved before the first change and restored on close. uninstall and clearing data only touch your own app's bundle id and apps warden installed.
Test with warden e2e
warden e2e smoke --physical --platform android
warden e2e smoke --udid 00008140-000A0B0C0D0E0F10physical: true or physical: { ids: […] } on a suite or a devices leg does the same; the flags win. A physical run never reboots, hides windows, arranges, slims or shuts down a device, and installs the release build kind by default. Mark a spec that needs two fingers with test(title, { requires: ["pinch"] }, body) and warden skips it on an iPhone.
On an iPhone --app (or a suite app) fails before anything is leased until device builds ship: install the app on the phone yourself.
iPhone signing (team)
The iPhone has no simulator driver: warden builds its own XCUITest runner and installs it on the phone, which needs an Apple developer team to sign with.
- Set
WARDEN_IOS_TEAM_ID=ABCDE12345orios: { teamId: "ABCDE12345" }inwarden.config.ts(the variable wins). - Without either, warden uses the keychain's
Apple Developmentteam when there is exactly one, and failsios_team_ambiguouslisting the teams otherwise. - A paid team gives one-year profiles. A free personal team gives 7-day profiles and a manual trust step, so expect rebuilds.
- The build is cached per team and Xcode build, and rebuilt for a new phone or when the profile ends within 24 hours.
warden doctorshows the team, the cache and the last failure.
Android notes
- Enable Developer options, then USB debugging, plug in the phone and accept the "Allow USB debugging?" prompt for this computer. Until you do, adb says
unauthorized. - Keep the screen unlocked or the lock set to Swipe/None. warden wakes a sleeping phone and dismisses a keyguard with no credential; a PIN, pattern or password fails
E_DEVICE_LOCKED. - Two adb servers (for example one from Android Studio and one from the SDK) make phones flicker between
offlineanddevice;warden doctorwarns,adb kill-serverand keep one. - After a sign-in Google may show Save password to Google Password Manager?. warden closes that sheet with Back for both
acceptanddismiss; it never presses Never or Continue, because those write to the Google account. Use a test account on the phone. abi_mismatchon install means thesimulatorkind (host CPU) reached an arm phone: use thedevorreleasekind.
iPhone notes
- Enable Developer Mode (Settings, Privacy & Security), then restart the phone. A paired phone with it off is listed as
developer mode off. - Trust this computer when asked, and trust the developer certificate under Settings, General, VPN & Device Management on first run (
runner_untrusted). - Turn on Enable UI Automation under Settings, Developer (
ui_automation_disabled). - Keep the phone unlocked and awake while a run starts (
device_locked). - One UI-test session per phone: Xcode or Appium on the same phone ends warden's runner. warden reports
device_busyand never kills another tool's xcodebuild. - Speed: expect about 0.8 s per tap (XCUITest waits for the app to go idle, roughly ten times slower than a simulator), about 0.15 to 0.3 s for a snapshot and about 2.5 s to launch an app.
screen appearanceis read-only on an iPhone, andscreen rotategoes throughdevicectl.
Privacy
Screenshots, snapshots, screen recordings and results.jsonl capture whatever is on the phone's screen: messages, notifications, names, account details. Use a test device or a clean profile, sign in with test accounts, and treat the batch directory (screen-at-failure.txt, screenshots, recordings) as personal data before attaching it to a CI artifact or an issue.
Diagnose
warden doctor # ios-team, ios-devices, ios-runner and android-devices rows appear only when a phone or a team is present
warden devices --physical| Row | Shows |
|---|---|
ios-team | the resolved team and where it came from, or ambiguous: N teams, set WARDEN_IOS_TEAM_ID |
ios-devices | per device: transport, pairing, Developer Mode, DDI, reachable or offline, locked |
ios-runner | the cached runner build, profile expiry and the last recorded failure |
android-devices | phones, how many are unauthorized or offline, and a second adb server |