Skip to content

fix(swift-ios): defer composer focus until after view updates - #12655

Closed
saphid wants to merge 305 commits into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:fix/swiftui-composer-focus-cycle
Closed

saphid wants to merge 305 commits into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:fix/swiftui-composer-focus-cycle

Conversation

@saphid

@saphid saphid commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

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.updateUIView calls becomeFirstResponderWhenAttached, which runs becomeFirstResponder inside SwiftUI's update pass. The focus change starts another graph update, and the main thread spins in AG::Graph::print_cycle indefinitely. The user cannot type or navigate until the app is killed. The unsafe path came from #7607 (efaec0bd8e), which replaced the @FocusState field with FeatureComposerTextInput. 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

  • Reproduced and fixed in the Simulator (iOS 27 Simulator, Debug build, paired to a disposable local server): open a thread, tap the composer, type, tap Back.
    • On 157476f1fb the app froze in 5 of 5 runs: 100% CPU, typed text never appears, Back ignored. A process sample shows the main thread in FeatureComposerTextInput.updateUIView → becomeFirstResponderWhenAttached → becomeFirstResponder → AG::Graph::print_cycle.
    • With this change the same script worked in 4 of 4 runs: text typed, CPU under 3%.
  • Focused tests with this change on 157476f1fb (iOS 27 Simulator): 69 passed across FeatureVoiceInputTests, FeaturePastedTextTests and FeatureComposerPowerTests. CI Contract fixtures and native tests passes 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.

Before: tapping the composer freezes the app

After: the composer focuses and accepts text.

After: the composer focuses and accepts typed text

Real-time recordings: before.mp4 · after.mp4.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

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

t3dotgg added 30 commits August 18, 2026 14:32
t3dotgg and others added 6 commits September 15, 2026 04:29
…-app-swift

# Conflicts:
#	.agents/skills/test-t3-mobile/SKILL.md
#	docs/user/appearance.md
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>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 19, 2026
@saphid
saphid marked this pull request as ready for review September 27, 2026 20:30
@macroscopeapp

macroscopeapp Bot commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 4f46c4d

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.

@saphid

saphid commented Sep 27, 2026

Copy link
Copy Markdown
Contributor Author

@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 4f46c4d722:

  • CI: all checks pass on this head.
  • iOS 27 Simulator: on upstream 157476f1fb, tapping the composer froze the app in 5 of 5 runs (100% CPU, SwiftUI graph cycle from becomeFirstResponderWhenAttached, matching the phone watchdog reports). With this PR, 0 of 4 valid runs froze and text typed normally (videos in the description). 69 focused tests pass.

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.

Copy link
Copy Markdown
Member

Note

This comment is posted by Julius' dot

Please clarify the intended upstream base for this composer-focus repair. The focused comparison shows the first-responder change and its test, but the complete PR targets 285d2ab and diverges at 901db89, including the Swift client foundation and other inherited work. It also diverges from the described test baseline 157476f1fb at 2eb6a53.

Which upstream baseline should receive the full contribution? Please restack there, or explain the intended cumulative integration and how the tested candidate maps to it. That is needed to assess the whole PR under one problem. Leaving this open pending clarification; this does not dismiss the reported composer freeze.

@t3dotgg
t3dotgg force-pushed the t3code/rebuild-mobile-app-swift branch from 1637f70 to cf79028 Compare October 1, 2026 21:36
t3dotgg pushed a commit that referenced this pull request Oct 2, 2026
Port the focused fix from #12655 onto the current SwiftUI branch.

Source-Commit: 4f46c4d

Ported by GPT-6.1-Sol through the Codex harness in T3 Code.
@t3dotgg

t3dotgg commented Oct 2, 2026

Copy link
Copy Markdown
Member

Note

🤖 GPT-6.1-Sol responding on behalf of Theo

Applied the focused fix to t3code/rebuild-mobile-app-swift in 6f1ae28e30. Closing this source PR after the port.

The source branch includes old rewrite history that conflicts with the current target. Only the intended fix and its focused tests were carried over.

The combined focused native checks passed: 241 tests, one existing skip, no failures.

@t3dotgg t3dotgg closed this Oct 2, 2026
t3dotgg pushed a commit that referenced this pull request Oct 7, 2026
Port the focused fix from #12655 onto the current SwiftUI branch.

Source-Commit: 4f46c4d

Ported by GPT-6.1-Sol through the Codex harness in T3 Code.
t3dotgg pushed a commit that referenced this pull request Oct 8, 2026
Port the focused fix from #12655 onto the current SwiftUI branch.

Source-Commit: 4f46c4d

Ported by GPT-6.1-Sol through the Codex harness in T3 Code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants