Skip to content

feat: Add iOS physical device support - #2856

Closed
omnarayan wants to merge 6 commits into
mobile-dev-inc:mainfrom
omnarayan:feature/ios-physical-device-support-clean
Closed

feat: Add iOS physical device support#2856
omnarayan wants to merge 6 commits into
mobile-dev-inc:mainfrom
omnarayan:feature/ios-physical-device-support-clean

Conversation

@omnarayan

@omnarayan omnarayan commented Nov 26, 2025

Copy link
Copy Markdown

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

- Add PORT environment variable support to XCTest HTTP server
- Implement real device detection and port forwarding in IOSDevice.kt
- Update LocalIOSDevice and DeviceControlIOSDevice for real device handling
- Update DeviceService client implementation
- Update binary artifacts for iOS simulator
- Update Xcode project configuration and build settings
When using --driver-host-port flag, maestro should connect to an external
driver server without attempting to validate or build iOS drivers locally.

Changes:
- Set connectToExistingSession=true when custom driver port is specified
- Skip driver validation for real iOS devices when using custom port
- Avoid calling isShutdown() when custom driver port is specified to
  prevent accessing local driver files

This allows users to connect to external XCTest servers without requiring
an Apple Team ID or local driver builds.
The xctestrun config files in repo are only used for bundled drivers,
not for xcodebuild commands.
When --driver-host-port is specified, skip SessionStore.hasActiveSessions()
check and set connectToExistingSession=false. This ensures a fresh driver
is started for all devices (Android, iOS simulator, iOS real device) when
using custom ports for parallel execution.
@Fishbowler

Copy link
Copy Markdown
Contributor

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.

@omnarayan

Copy link
Copy Markdown
Author

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

Copy link
Copy Markdown
Author

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.

@yerffejytnac

This comment has been minimized.

@mkatse78

This comment has been minimized.

@Fishbowler

Copy link
Copy Markdown
Contributor

The Maestro team are going to be working on fuller iOS device support later this quarter. This going to provide a great step up!

@omnarayan

Copy link
Copy Markdown
Author

@mkatse78 @yerffejytnac — Stopgap solution available here: https://github.com/devicelab-dev/maestro-ios-device

@omnarayan

This comment has been minimized.

@mobile-dev-inc mobile-dev-inc deleted a comment from omnarayan Jan 19, 2026
@JamesJ-P

Copy link
Copy Markdown

Thank you all for your great work. Has there been any progress on support for real iOS devices? I'm facing an issue where the iOS simulator does not support Bluetooth functionality, but my test app requires Bluetooth, so I have to turn to real iOS devices.

@nopsled

nopsled commented May 24, 2026

Copy link
Copy Markdown

Any updates here?

ld83luis1-ui

This comment was marked as off-topic.

@phazei

phazei commented Jun 10, 2026

Copy link
Copy Markdown

As the recommended test path for all of react-native, it's disappointing that no react-native apps can have actual proper testing on a physical iOS device until we get device support here.

@Fishbowler

Copy link
Copy Markdown
Contributor

Given this is a partial implementation that isn't advancing, I'm gonna close this for now.
When the team comes to work on this, this (and others) can serve as useful ground work. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support real iOS devices

8 participants