Skip to content

fix(apple): avoid blocked interactions on physical iOS#1263

Merged
thymikee merged 5 commits into
callstack:mainfrom
billsbooth:codex/ios-system-modal-alerts
Jul 15, 2026
Merged

fix(apple): avoid blocked interactions on physical iOS#1263
thymikee merged 5 commits into
callstack:mainfrom
billsbooth:codex/ios-system-modal-alerts

Conversation

@billsbooth

@billsbooth billsbooth commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • skip target-app activation preflight for iOS alert commands so system prompt inspection/dismissal can run while SpringBoard owns a blocking modal
  • let foreground cached coordinate-only taps avoid unnecessary target-app activation, while missing, changed, or background targets stay on the normal activation path
  • route alert and coordinate-only taps to SpringBoard only when a blocking iOS system modal is visible
  • keep selector/text interactions, scroll, drag, and sequence commands on the existing foreground/AX path
  • skip XCUIApplication.activate() when a newly created runner process resolves a target that is already foreground

Why

A physical-iPhone permission dialog can be owned by SpringBoard while covering the target app. The runner previously accepted public coordinate tap commands, but prepared them by activating the target app first. XCTest can hang or abort in that activation/quiescence step while the SpringBoard modal is visible, so the command never reaches gesture synthesis. Screenshots still work because they do not need target-app activation.

The coordinate-only fast path is intentionally limited to an already foreground cached target. That preserves the existing activation safety net for ordinary no-modal commands, while the explicit SpringBoard-modal route can still bypass target-app activation when a native modal is actually blocking interaction.

There is a second form of the same activation failure. A fresh XCTest process has no cached bundle ID, so command preparation takes the bundle_changed path even when the requested app is already foreground. Calling activate() in that state can wait indefinitely for a continuously updating React Native app to become idle. Reading the app state first and skipping only the redundant foreground activation preserves normal launch/background recovery while allowing the command to reach gesture delivery.

Verification

  • Rebased onto current origin/main (6efe54451)
  • git diff --check
  • pnpm format:check
  • pnpm typecheck
  • pnpm test (438 files, 4,041 tests)
  • pnpm build:xcuitest:ios
  • Physical-device native-prompt validation before rebase:
    • alert get detected a native notification prompt and its buttons
    • alert dismiss cleared the prompt
    • alert get with no prompt returned alert not found promptly instead of timing out
    • public coordinate press opened the target app settings surface while the app was otherwise automatable
  • Physical-device foreground-target validation on the rebased branch:
    • reproduced main thread execution timed out after AGENT_DEVICE_RUNNER_ACTIVATE ... reason=bundle_changed while Dibs was already foreground
    • rebuilt and signed the local XCTest runner with the fix
    • five navigation taps from fresh runner processes logged AGENT_DEVICE_RUNNER_ACTIVATE_SKIPPED ... reason=already_foreground and COMMAND_COMPLETED ... ok=1

Remaining note

Accessibility snapshots can still time out later while traversing a continuously updating React Native view. That is separate from the pre-delivery activation failures fixed here; screenshots and coordinate taps remain the practical fallback when AX traversal itself cannot settle.

Refs #1246

@billsbooth billsbooth force-pushed the codex/ios-system-modal-alerts branch 2 times, most recently from 6ee5113 to 5d0b951 Compare July 14, 2026 18:02
@thymikee

Copy link
Copy Markdown
Member

Review concern on exact head 5d0b951.

The intended system-modal path is supported by the physical-device evidence, but coordinate-only taps now bypass target-app activation before checking hasCachedTargetForActivationSkip. That broadens the behavior beyond the reported foreground-app recovery case: when the requested app is not cached or no longer foreground and no blocking SpringBoard modal is detected, resolveAppWithoutActivation can return an unactivated XCUIApplication and the coordinate gesture may execute against the wrong visible surface.

Please retain the cached-target and foreground guard for the ordinary no-modal coordinate path, while keeping the explicit SpringBoard-modal route able to bypass it. Add contrasts proving:

  • foreground cached target plus coordinate-only tap skips activation;
  • blocking system modal routes to SpringBoard;
  • missing, changed, or background target does not silently bypass activation.

CI smoke and Swift jobs are also still pending, so this head is not merge-ready yet.

@billsbooth billsbooth force-pushed the codex/ios-system-modal-alerts branch from 5d0b951 to 8f9102a Compare July 15, 2026 02:47
@billsbooth billsbooth changed the title fix(apple): handle system alerts on physical iOS fix(apple): avoid blocked interactions on physical iOS Jul 15, 2026
@thymikee thymikee merged commit a458815 into callstack:main Jul 15, 2026
17 checks passed
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.

2 participants