Conversation
Fix the synchronous responder path introduced by pingdotgg#7607 (efaec0b). Carry the tested two-file fix from 45d15ad7ee onto the upstream SwiftUI branch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a contained fix to existing iOS composer focus handling that moves responder changes outside SwiftUI’s update pass and guards stale requests. The only other change updates a focused test to observe the deferred main-queue behavior. You can add or adjust custom eligibility rules. Learn more. |
|
@t3dotgg this is ready for your review and merge. Merge order: independent of the SwiftUI thread-sync chain (#10758 → #10759 → #10761 → #10762 → #10763 → #10764 → #10765 → #10766 → #10767); it can merge at any time. Evidence at head
Independent review: GPT-6 Astra xhigh (read-only) found no defects. Its only note was a missing unit test for responder timing, which was declined because it needs a hosted UIKit responder harness; the Simulator evidence above covers the behaviour. |
What Changed
Defer native composer first-responder changes until after SwiftUI finishes the current view update. Superseded focus requests are ignored, and pending attachment focus is cancelled as soon as focus is cleared. The keyboard-preservation test now observes focus after the queued change runs.
Why
Tapping the composer can freeze the app.
FeatureComposerTextInput.updateUIViewcallsbecomeFirstResponderWhenAttached, which runsbecomeFirstResponderinside SwiftUI's update pass. The focus change starts another graph update, and the main thread spins inAG::Graph::print_cycleindefinitely. The user cannot type or navigate until the app is killed. The unsafe path came from #7607 (efaec0bd8e), which replaced the@FocusStatefield withFeatureComposerTextInput. Physical-iPhone watchdog reports show this exact stack.Moving responder changes outside the update pass, and checking that a queued request still matches the current binding before applying it, breaks the cycle. This affects the native SwiftUI composer on iPhone and iPad only; no provider, wire contract, or connection-mode change.
Verification
157476f1fbthe app froze in 5 of 5 runs: 100% CPU, typed text never appears, Back ignored. A process sample shows the main thread inFeatureComposerTextInput.updateUIView→becomeFirstResponderWhenAttached→becomeFirstResponder→AG::Graph::print_cycle.157476f1fb(iOS 27 Simulator): 69 passed acrossFeatureVoiceInputTests,FeaturePastedTextTestsandFeatureComposerPowerTests. CIContract fixtures and native testspasses on the head.UI Changes
No layout change. Same scripted flow on both builds.
Before (
157476f1fb): the composer tap freezes the app; typing and Back do nothing.After: the composer focuses and accepts text.
Real-time recordings: before.mp4 · after.mp4.
Checklist
Implementation: Claude Fable 5.1 in the original T3 Code Claude agent thread. Handoff and PR preparation: GPT-6 Astra in Codex. Simulator reproduction and verification: Claude Opus 5.5 in Claude Code.
🤖 Generated with Claude Code