fix(ios): recognize and answer the localized Open in launch confirmation
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, tableSchemeApproval. 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.
OuvrirandAnnulernow translateOpenandCancelin every preferred language, soalert acceptandalert dismissanswer the prompt. Matching stays exact and #3715's rule holds: no guessed label. - New
RunnerLaunchConfirmationPromptrecognizes the prompt from the OS's own title format and both buttons of one language. The runner setslaunchConfirmationPrompt: trueon that alert read, andopenaccepts a marked prompt. This replaces the TypeScript English title and button match. Unreadable strings recognize nothing, so the prompt staysalert-unrecognized. - A shared
RunnerSystemPromptStringsreader 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-FRandja-JPrecognize the prompt, andacceptpicksOuvrirand開くwhiledismisspicksAnnuler; 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:
acceptreturnsnilfor["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 --relaunchreturnedlaunchConfirmation: "accepted".- With
xcrun simctl openurlraising the prompt again,alert getreturnedmessage: "Ouvrir dans « Agent Device Tester » ?",items: ["Ouvrir", "Annuler"],launchConfirmationPrompt: true.alert acceptreturnedaccepted, and the nextalert getfound no alert. - The language was restored to the original
en-US, pl-US, es-PL, zh-Hans-PL, fi-PLanden_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