fix(ios): recognize and answer the localized Open in launch confirmation

Merged
TH
1d ago
+441-77

Summary

Fixes #3922. On a non-English iOS simulator, the Open in "<App>"? confirmation raised by open --launch-url stayed alert-unrecognized, and alert accept refused Ouvrir with ALERT_BUTTON_NOT_FOUND. This extends #4006's mechanism to that prompt. No new flag or command.

  • SpringBoard's strings live in CoreServices.framework, table SchemeApproval. The runner already reads that framework's simulated-OS root, so the table is readable. It holds the title format (SCHEME_APPROVAL_PROMPT_TITLE_NO_SOURCE) and the open and cancel labels.
  • The table joins the vocabulary tables of #4006. Ouvrir and Annuler now translate Open and Cancel in every preferred language, so alert accept and alert dismiss answer the prompt. Matching stays exact and #3715's rule holds: no guessed label.
  • New RunnerLaunchConfirmationPrompt recognizes the prompt from the OS's own title format and both buttons of one language. The runner sets launchConfirmationPrompt: true on that alert read, and open accepts a marked prompt. This replaces the TypeScript English title and button match. Unreadable strings recognize nothing, so the prompt stays alert-unrecognized.
  • A shared RunnerSystemPromptStrings reader serves both consumers.

Behavior to know: alert get now also prints launchConfirmationPrompt: true for this prompt, because the runner payload passes through. It is documented in the alert section of commands.md. Say so if you want it stripped.

Tests

  • Swift (UnitTests/RunnerTests+LaunchConfirmationPromptTests.swift), run on the iPhone 17e simulator:
    • pure wordings (English, French, Japanese) are recognized, with negatives for foreign buttons, mixed languages, and extra buttons;
    • OS strings for fr-FR and ja-JP recognize the prompt, and accept picks Ouvrir and 開く while dismiss picks Annuler; German is not recognized, because it is not a preferred language;
    • English stays recognized and accepted.
  • Before the fix (table removed), the same test fails: accept returns nil for ["Annuler", "Ouvrir"].
  • TypeScript: French and Japanese marked prompts are accepted. Before the fix, the same two cases fail. An unmarked launch-like alert stays unrecognized.

Live check (iPhone 17e, F25C7515, iOS 27.0)

French set per simulator (AppleLanguages ("fr"), AppleLocale fr_FR) and rebooted, with a copy of the test app that has a fresh URL scheme (the lab app's schemes were already approved):

  • open <app> --launch-url adt3922fr://x --relaunch returned launchConfirmation: "accepted".
  • With xcrun simctl openurl raising the prompt again, alert get returned message: "Ouvrir dans « Agent Device Tester » ?", items: ["Ouvrir", "Annuler"], launchConfirmationPrompt: true. alert accept returned accepted, and the next alert get found no alert.
  • The language was restored to the original en-US, pl-US, es-PL, zh-Hans-PL, fi-PL and en_US@rg=plzzzz, the test copy was uninstalled, the session was closed, and the simulator was shut down.

iOS and macOS runner builds pass. check:xctest-selection and check:packaged-runner-swift pass.

Validation

pnpm check:affected --run --head f862d9ef37 (exactCommit=true, run-nCOquN): every stage passed. Later heads differ in Swift files and one docs sentence only. On af69d176bd, three gate runs passed format, lint, typecheck, layering, fallow, native-dupes, build, check:packaged-runner-swift, check:xctest-selection, and the docs checks. Each run's unit stage then failed on one or two different unrelated tests, such as daemon-client-lifecycle, provider-revyl/runtime, daemon-registration-owner, and snapshot-helper-exit-result. All pass alone, so I read them as host contention. The Swift edits are covered by the simulator tests below, which pass.

Review round 3 (Finnish %[tt]@, SpringBoard-only mark, missing-table test) is covered by new simulator tests: one loads every installed SchemeApproval localization and checks each rendered title is recognized, one covers the missing-table branch, and one checks an app-owned alert with identical text is not marked. Before the fix, the Finnish, every-localization, and app-owned cases fail.

Live on the iPhone 17e with fresh unapproved schemes, French and Finnish: open --launch-url returned launchConfirmation: "accepted", alert get returned launchConfirmationPrompt: true (Ouvrir dans « Agent Device Tester » ? with Ouvrir/Annuler; Avataanko Agent Device Tester-apissa? with Avaa/Kumoa), and alert accept answered it. The language list was restored afterwards.

CI owns the rest.

Risks: the string-table location is undocumented, as in #4006. A runner older than this change does not set launchConfirmation, so open reports its prompt as unrecognized until the runner is rebuilt.

🤖 Generated with Claude Code