Skip to content

fix(ios): broadcast cold start fails on real devices (breadcrumb button, picker foreground, slow dump) - #431

Merged
gmegidish merged 2 commits into
mainfrom
fix/ios-broadcast-button-breadcrumb
Sep 19, 2026
Merged

gmegidish merged 2 commits into
mainfrom
fix/ios-broadcast-button-breadcrumb

Conversation

@gmegidish

@gmegidish gmegidish commented Sep 19, 2026

Copy link
Copy Markdown
Member

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 in clickStartBroadcastButton / launchDeviceKitApp.

1. expected exactly one button on 'Press to Start Broadcasting' screen, found 2

When 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. findRecordButton now picks the picker view's own ModuleIcon button and only falls back to "exactly one button".

2. timeout waiting for app com.mobilenext.devicekit-h264 to be in foreground

The devicekit app taps its own picker button in onAppear. The picker is presented by SpringBoard, so polling the active app goes Settings → SpringBoard and the app itself is never reported (3/3 on a fresh install).

The foreground wait is removed. clickStartBroadcastButton already polls until the picker's BroadcastUploadExtension entry 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. waitForAppInForeground and deviceKitAppLaunchTimeout had no other callers and are deleted.

3. timeout waiting for BroadcastUploadExtension button to appear

DumpSource first 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 LLDB 14:59:27 → LLDB finished 14: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

  • Unit tests for findRecordButton: breadcrumb ignored, single button accepted whatever its name, ambiguous / no buttons rejected
  • go vet ./... && go test ./... -race
  • iPhone, fresh launch from Settings (app auto-opens the picker): launch 15:49:24.6 → picker entry found 15:49:26.5 → Start Broadcast found 15:49:27.6 → capture streaming
  • iPhone, resumed app from Settings (breadcrumb, no auto-tap): launch 15:50:14.2 → Tapping record button 15:50:15.7 → picker entry found 15:50:16.7 → Start Broadcast found 15:50:17.8 → capture streaming
  • No LLDB injection in either log
  • Not covered: the Flutter LLDB probe still costs ~20s on any debuggable non-Flutter foreground app for a normal dump ui

Summary by CodeRabbit

  • Bug Fixes

    • Improved accessibility handling for the iOS broadcast picker.
    • Increased the time available for the picker to appear during app launch.
    • Improved record-button selection by avoiding status-bar elements and handling unambiguous fallback buttons.
  • Tests

    • Added coverage for record-button selection, including breadcrumbs, fallback buttons, and ambiguous or missing button scenarios.

… 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.
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: mobile-next/mobilecli/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: 4294f6ae-78c4-4643-9761-54f615daece8

📥 Commits

Reviewing files that changed from the base of the PR and between 0c400c3 and 2133d76.

📒 Files selected for processing (2)
  • devices/ios.go
  • devices/ios_elements_test.go

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.


📝 Walkthrough

Walkthrough

The 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 ModuleIcon or a unique fallback button, with focused tests.

Changes

iOS broadcast picker handling

Layer / File(s) Summary
Broadcast picker readiness and accessibility
devices/ios.go
Broadcast picker polling uses the direct accessibility-tree reader. Initial polling allows 15 seconds. DeviceKit app launch returns without foreground detection, and obsolete foreground polling logic was removed.
Record-button selection and validation
devices/ios.go, devices/ios_elements_test.go
Record-button lookup prefers ModuleIcon, falls back to exactly one button, and returns errors for missing or ambiguous candidates. Tests cover breadcrumb exclusion and fallback behavior.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Suggested reviewers: hakanor

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: fixes for iOS broadcast cold-start failures involving breadcrumb button selection, picker foreground handling, and slow accessibility-tree dumping.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

… 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.
@gmegidish
gmegidish merged commit e2af8ef into main Sep 19, 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.

1 participant