maestro icon indicating copy to clipboard operation
maestro copied to clipboard

feat: Add iOS physical device support

Open omnarayan opened this issue 1 month ago • 4 comments

Proposed changes

copilot:summary

This PR adds support for running Maestro tests on physical iOS devices.

I didn't want to build something that restricts users to a single device, so this works for both Android and iOS, and naturally enables concurrent test execution on multiple devices.

I've only modified the core test runner to accept a custom port. The orchestration layer (how Maestro manages building for real devices, test runner and port forwarding) is intentionally left unchanged - I have already on thin ice by my opinion to add multiple device support.

Changes are organized in separate commits for easy cherry-picking.

Why we built this

We needed this for our solution offering and wanted to give it back to the community.

Credit: Builds upon the approach proposed by @avinash-bharti in #2339.

Usage: iOS Physical Device

1. Build XCTest Runner for Device

cd maestro-ios-xctest-runner

xcodebuild clean build-for-testing \
    -project maestro-driver-ios.xcodeproj \
    -scheme maestro-driver-ios \
    -destination 'platform=iOS,id=<DEVICE_UDID>' \
    -derivedDataPath Build \
    DEVELOPMENT_TEAM=<YOUR_TEAM_ID>
  1. Start XCTest Server on Device
  xcodebuild test-without-building \
      -xctestrun ./Build/Build/Products/maestro-driver-ios_iphoneos*.xctestrun \
      -destination "platform=iOS,id=<DEVICE_UDID>" &
  1. Start Port Forwarding

The XCTest server runs on port 22087 on the device. Forward it to your machine using any of these tools:

Using iproxy (libimobiledevice):

  iproxy 22087:22087 -u <DEVICE_UDID>

Using pymobiledevice3:

  pymobiledevice3 usbmux forward --udid <DEVICE_UDID> 22087 22087

Using go-ios:

  ios forward 22087 22087 --udid <DEVICE_UDID>

Using Xcode's devicectl:

  xcrun devicectl device forward --device <DEVICE_UDID> localPort=22087 remotePort=22087
  1. Run Maestro Tests
  maestro --driver-host-port 22087 --device <DEVICE_UDID> test flow.yaml

Tested on

  • iOS real device + iOS simulators

  • iOS real device + Android real device

  • iOS real device + Android real device + Android emulator

  • iOS real device + iOS simulator + Android real device

  • Multiple iOS real devices in parallel with different OS versions (iOS 18.x and iOS 26.x)

    Issues fixed

Fixes #686

omnarayan avatar Nov 26 '25 10:11 omnarayan

Thanks! This looks like a good step forwards.

Does this work with addMedia or setLocation? Will setting permissions work? I think there's a bunch of simctl invocations in the codebase that won't work on a real device.

Fishbowler avatar Nov 26 '25 10:11 Fishbowler

addMedia or setLocation

I haven't had a chance to test it thoroughly yet — my main focus was just getting it to work. There's still a lot more that can be done, and I'd love to improve it further when time permits (honestly, mostly driven by my own needs 😅). For now, I'll focus on making it easier for others to use. Any feedback or suggestions are welcome

omnarayan avatar Nov 26 '25 10:11 omnarayan

To clarify (my previous comment may have been confusing): we have tested this on multiple devices with different iOS versions, but the following commands are not supported in this PR:

  • addMedia
  • setLocation
  • clearState
  • permissions

Status of each:

  • addMedia / setLocation / permissions: No solution currently.

  • clearState: We have a workaround, but it requires Maestro to be aware of the IPA file.

Still debating the best approach, adding a CLI arg feels messy since it's only needed for physical devices.

Alternative: provide a separate build along with our opinionated approach for building the Runner app for physical devices, starting it, and handling port forwarding, until Maestro has official support.

omnarayan avatar Nov 29 '25 20:11 omnarayan

So excited for when this lands! 🙌

yerffejytnac avatar Dec 17 '25 03:12 yerffejytnac