Skip to content

feat: auto-dismiss the stale Screen Broadcasting alert during broadcast start/stop - #24

Merged
Timo972 merged 6 commits into
timo/dro-2713-wda-runner-reliability-under-capture-loadfrom
timo/dro-2789-dismiss-screen-broadcasting-alert
Aug 25, 2026
Merged

feat: auto-dismiss the stale Screen Broadcasting alert during broadcast start/stop#24
Timo972 merged 6 commits into
timo/dro-2713-wda-runner-reliability-under-capture-loadfrom
timo/dro-2789-dismiss-screen-broadcasting-alert

Conversation

@Timo972

@Timo972 Timo972 commented Aug 24, 2026

Copy link
Copy Markdown

Stacked on #23.

What

When a ReplayKit broadcast ends (the runner died mid-broadcast, or the broadcast was stopped programmatically), iOS 26 posts a modal system alert ("Screen Broadcasting", OK / Go to Application). While that alert is up, the broadcast picker flow cannot complete, so automated broadcast restarts fail until the alert is dismissed by hand.

This teaches the agent to dismiss it itself:

  • broadcast/start and broadcast/stop accept an optional dismissButtonLabels argument (default ["OK"], pass localized labels on non-English devices — same contract as confirmButtonLabels).
  • During the start flow's confirmation-button wait, a matching system-alert button is tapped (via the same synthesized-tap path as the confirmation tap, now a shared helper) and the flow continues; the check also runs while waiting out an already-active capture, since dismissing the alert is what releases the stale capture flag.
  • After a successful programmatic stop, the agent waits briefly for the alert and clears it best-effort, so a stop/start cycle leaves nothing behind. The stop's result is never affected.
  • Only a button exactly matching the dismiss labels is ever tapped — the alert's "Go to Application" action is structurally unreachable, and a non-matching alert is left alone.

Tests

No unit seam exists for this behavior (it is XCUI against SpringBoard); the full unit bundle (175 green) and iOS/watchOS builds serve as regression, and the three on-device acceptance scenarios (pre-existing alert → start succeeds; stop→start cycle leaves no alert; first-ever start unchanged) are device-verified separately. Docs updated in docs/broadcast-extension.md.

🤖 Generated with Claude Code

Timo972 and others added 6 commits August 24, 2026 16:14
…is acknowledgement

The alert-dismissal tap synthesized inside dismissBroadcastStoppedAlertWithLabels:
previously waited synchronously for the synthesis acknowledgement (up to the
event-synthesis timeout margin, ~15s, when the system sheds the event). That
call runs inside run-loop spinners with much shorter deadlines (the ~3s
post-stop sweep, the 5s already-captured wait, the confirm spin), and a
spinner deadline cannot interrupt a nested synchronous call - so under the
exact overload scenario these bounds exist for, the automation queue could
stay blocked ~15s.

The dismissal doesn't need the acknowledgement: every caller re-checks the
alert's existence on its next spin iteration, so the tap is now
fire-and-forget via a new FBXCTestDaemonsProxy +synthesizeEventAsyncWithRecord:
that dispatches without waiting, logging (and otherwise ignoring) failures.
A 1s re-attempt cooldown avoids re-tapping the same coordinates while the
alert's dismissal animation is still in flight. The CONFIRM tap stays
synchronous and untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The isCaptured-wait loop's dismissal log still said "dismissed" from before
the dismissal tap became fire-and-forget; bring it in line with the other
two dismissal log sites updated in 14457dd.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…it is gone

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… stop sweep

The post-stop sweep previously exited YES the instant no matching alert was
present, but SpringBoard publishes the iOS 26 "Screen Broadcasting" alert
with a delay after the extension socket closes, so the sweep saw nothing and
exited at t≈0, leaving the alert armed for the next start.

Rework the sweep into a two-phase wait, gated on iOS 26+ (the alert does not
exist on older iOS, where the start-side dismissal remains the safety net):
wait out a 2s appearance grace period if the alert has not been seen yet,
then once it has been seen, spin (retrying the dismissal tap, paced by the
existing 1s cooldown) until it is observed gone, capped at a 5s overall
timeout. The sweep stays best-effort and never affects the method's return
value.
@Timo972
Timo972 merged commit 60350e3 into master Aug 25, 2026
38 of 42 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