fix(ios): broadcast cold start fails on real devices (breadcrumb button, picker foreground, slow dump) - #431
Conversation
… modes Starting the ReplayKit broadcast on a real device failed in three ways: - "expected exactly one button ... found 2": when the devicekit app is opened from another app, iOS adds a "Return to <app>" button to the status bar. Pick the picker's own "ModuleIcon" button, falling back to the only button. - "timeout waiting for app ... to be in foreground": the app opens the broadcast picker as soon as it appears, and the picker belongs to SpringBoard, so the app itself is often never reported as active. Accept the picker as proof the app launched. - "timeout waiting for BroadcastUploadExtension button": DumpSource first probes the foreground app for a Flutter VM by injecting an agent over LLDB. On the devicekit app that attach succeeds, pauses the app and takes ~20s, longer than the whole 10s picker timeout. Read the accessibility tree directly.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: mobile-next/mobilecli/.coderabbit.yaml Review profile: CHILL Plan: Essentials Run ID: 📒 Files selected for processing (2)
Included review availability: 1 review is currently available. Your included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour. 📝 WalkthroughWalkthroughThe iOS broadcast picker now reads accessibility elements directly, polls for up to 15 seconds during initial readiness, and no longer waits for foreground detection. Record-button selection uses ChangesiOS broadcast picker handling
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
… poll The app opens the system broadcast picker as soon as it appears and SpringBoard owns that picker, so waiting for the app to be reported as active was racing the app against itself. Accepting SpringBoard's private picker view controller class worked but tied us to an Apple class name. clickStartBroadcastButton already polls until the picker's entry shows up and taps the record button when the app screen is visible instead, which covers both outcomes. Its timeout grows from 10s to 15s to absorb the old 5s wait.
Summary
Starting the ReplayKit broadcast on a real iPhone (
screencapture --format avc,screenrecord) failed in three different ways. All three were reproduced on an iPhone (iOS 26.5) and are fixed inclickStartBroadcastButton/launchDeviceKitApp.1.
expected exactly one button on 'Press to Start Broadcasting' screen, found 2When the devicekit app is opened from another app, iOS adds a "Return to Settings" breadcrumb button to the status bar, so "the only button" is not the record button.
findRecordButtonnow picks the picker view's ownModuleIconbutton and only falls back to "exactly one button".2.
timeout waiting for app com.mobilenext.devicekit-h264 to be in foregroundThe devicekit app taps its own picker button in
onAppear. The picker is presented by SpringBoard, so polling the active app goesSettings → SpringBoardand the app itself is never reported (3/3 on a fresh install).The foreground wait is removed.
clickStartBroadcastButtonalready polls until the picker'sBroadcastUploadExtensionentry shows up, and taps the record button whenever the app screen is visible instead — that covers both outcomes without depending on which process iOS reports as active (or on a private SpringBoard class name). The poll's timeout grows from 10s to 15s to absorb the old 5s wait.waitForAppInForegroundanddeviceKitAppLaunchTimeouthad no other callers and are deleted.3.
timeout waiting for BroadcastUploadExtension button to appearDumpSourcefirst probes the foreground app for a Flutter VM by injecting an agent over LLDB. The devicekit app is debuggable, so the attach succeeds, pauses the app, and takes ~20s (running LLDB14:59:27 →LLDB finished14:59:46) before falling back to the 0.46s accessibility dump — longer than the whole picker timeout. The picker poll now reads the accessibility tree directly; the picker is never Flutter.Test plan
findRecordButton: breadcrumb ignored, single button accepted whatever its name, ambiguous / no buttons rejectedgo vet ./... && go test ./... -raceTapping record button15:50:15.7 → picker entry found 15:50:16.7 → Start Broadcast found 15:50:17.8 → capture streamingdump uiSummary by CodeRabbit
Bug Fixes
Tests