nativeTap function
Taps native (non-Flutter) UI elements using platform-specific tools.
On Android this goes through adb shell input tap, which reaches any
on-screen UI regardless of process. --text finds the element in a
uiautomator dump first (see android_native_tap.dart); --logical
scales the coordinates by the device pixel ratio.
On iOS simulator this compiles (once, cached) a small Swift helper that
injects the touch through SimulatorKit's HID client, so it reaches every
process on screen, including SpringBoard system dialogs ("Allow location
access?", "Open in Test App?", etc.). When the helper cannot be used (no
Xcode toolchain, unknown device, compile failure...) it falls back to the
in-process UIApplication.sendEvent() path (the same one fdb tap --at
uses), which cannot reach SpringBoard; the result carries the reason so
the CLI adapter can warn. --text reads the simulator's accessibility
tree with the same helper and taps the matched element's frame center
(see ios_simulator_native_tap.dart); it has no fallback.
Never throws; all error conditions are represented as sealed result cases.
Implementation
Future<NativeTapResult> nativeTap(NativeTapInput input) async {
final platformInfo = readPlatformInfo();
if (platformInfo == null) return const NativeTapNoSession();
final platform = platformInfo.platform;
final isEmulator = platformInfo.emulator;
if (platform.startsWith('android')) {
return nativeTapAndroid(input, deviceId: readDevice());
}
if (platform.startsWith('ios') && isEmulator) {
if (input.text != null) return nativeTapIosSimulatorText(input, udid: readDevice());
// --logical is a no-op here: iOS coordinates are already points.
return _tapIosSimulator(x: input.x!, y: input.y!);
}
if (platform.startsWith('ios') && !isEmulator) {
return NativeTapPhysicalIosUnsupported(x: input.x, y: input.y);
}
if (platform.startsWith('darwin')) {
return NativeTapMacosUnsupported(x: input.x, y: input.y);
}
return NativeTapPlatformUnsupported(platform);
}