Repository navigation
perf(ios): confirm fill focus from the tapped input before the keyboard - #3906
Conversation
After a focus tap with no keyboard up, stabilization asked for the keyboard first. While a software keyboard is presented, that query blocks and then finds none: 0.68 s on an iPhone 17 Pro simulator and 0.57-3.77 s on CI, before the app-wide focus query confirmed focus anyway and the refresh point read the input again. Read the tapped input first and accept its own hasKeyboardFocus, which is stronger evidence than the keyboard's arrival; that read's frame serves the refresh point. The keyboard and the app-wide focus query stay as fallbacks. The runner log names the evidence that confirmed focus. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Where a fill's runner time goesMeasured on my own iPhone 17 Pro / iOS 27.0 simulator (test app, warm), plus the runner log of CI perf run 39679264668 at main before #3882. Phase durations in ms come from
There are no fixed sleeps beyond the 20 ms polls. Two kinds of cost remain:
Considered and not shipped
Pre-existing issue (both revisions)
Depends on #3882Hand the read that confirmed focus to the entry as its post-focus read. On #3882's stack, that lowered the fixture's value-read count for a hidden-keyboard fill from 22 to 19, pinned by a test. This PR passes only that read's frame to the refresh point. 🤖 Generated with Claude Code |
|
Thanks for the focus-from-the-tapped-input idea. At fcc403f, the keyboard-down fill route is the right one to make common, but the fix needs one change before it can merge. Not blocking, and fine to take or leave: Could the same result come from something smaller on main? In All 6 checks pass at fcc403f, but the PR has merge conflicts with main, so CI has not run the combined code. The conflicts are in |
Re-express the input-first focus confirmation in #3882's read model. The tapped input's read is a ResolvedTextEntryInput; when its snapshot reports keyboard focus, that same read gives the refresh point and seeds the TextEntryReader as the focus step's post-focus read. keyboardFocusedInputFrame and the frame-only refresh path are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Size Report
Startup median (7 runs, lower is better):
|
|
The read-reuse defect from the earlier review (#3906 (comment)) is now fixed in the merge: the keyboard-down early return hands the confirming read on as the post-focus read at TextEntryFocus.swift:153-154. Two things remain before merge at 3244fec. All 16 checks pass, and I found no other code problem in the diff, which only touches iOS runner Swift. No test fails if that hand-off is dropped. The new stabilization test only checks that The live validation is also out of date. The PR body table was measured at fcc403f against base e3ed027, before #3882 landed and before I did not run the Swift unit tests or a simulator. I also could not tell whether the keyboard-down tests ran or skipped in CI, because the simulator may keep the software keyboard down. It is not verified whether secure-text and WKWebView inputs report keyboard focus on their own snapshot. If they do not, they fall back to the old checks, which only costs performance. No conflicts are known. |
The tapped input's read rides in TextEntryFocusEvidence.inputReportsKeyboardFocus, so the evidence and the post-focus read cannot disagree. One read per poll serves both the input's own focus check and the app-wide focus query's expected snapshot. The keyboard-raising fill test skips before it asserts and pins the fixture's value-read count at 18 (17 measured, 19 when the entry resolves the input again). Device timings move from comments to the PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Removing the focus step's hand-off and dropping the read's seeding both raise the fixture count from 17 to 19. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Both reviews are addressed at 954ce83: 3244fec merges main, d9065c5 applies the notes, and 954ce83 fixes one test comment. Blocking: the focus-confirming read is the post-focus read. Read-count pin.
Take-or-leave notes.
Live evidence. Measurements ran at d9065c5, whose production code matches 954ce83, on my own iPhone 17 Pro / iOS 27.0 simulator. The PR body has the full table. One Bluesky logged-out username fill at head shows the focus read, the evidence line, and then the first type dispatch:
🤖 Generated with Claude Code |
|
This PR is ready at 954ce83. Both asks from the earlier review are done: the keyboard-down read-count pin now runs through Not blocking, take or leave: the comment at RunnerTests+TextEntryLatencyTests.swift#L121 says the pin has "one read of headroom", but one I did not run the Swift unit tests or a simulator. The 19-reads claim comes from tracing the two counted queries, not from a mutation run. I can't tell whether CI's simulator keeps the software keyboard down; if it does not, both keyboard-down tests skip there and only local runs enforce the pin. The live timings and the 39-fill counts are from the author's own iPhone 17 Pro / iOS 27.0 simulator, and only one runner.log excerpt (Bluesky) is quoted. I have not verified that secure-text and WKWebView inputs report keyboard focus on their own snapshot. A miss there falls back to the old checks, so it costs only speed. |
|
* origin/main: perf(ios): confirm fill focus from the tapped input before the keyboard (#3906)
* origin/main: perf(ios): confirm fill focus from the tapped input before the keyboard (#3906) feat(perf): time every iOS command against the fixture app (#3885) fix(ios): stop the XCTest-channel penalty from re-arming itself (#3896) test(android): pin that a child vanishing mid-read reads as gone (#3905) ci: skip device smoke lanes when a PR changes no lane input (#3904) fix(ios): verify deep-tree back on the sole screen (#3894) fix(ios): synthesize long presses and the gesture viewport on deep trees (#3893) perf(ios): read each resolved text input through one snapshot (#3882) fix(gate): take the host validation lock for heavy gates (#3900) fix(logs): keep an early iOS simulator logs stop from hanging the session (#3892) fix(apple): clean the runner derived path the runner uses (#3897) test(provider-scenarios): open deadline scenarios without the test IME (#3903) test(host-kit): stub os.homedir for the exec-trace late-close test (#3898) feat(test-app): add a Bluesky-depth heavy feed screen (#3884)
* test(ios): hold the fill read-count pin to the route it measures The pin allowed 18 value reads for a fill whose focus tap raises the software keyboard, calling the extra read headroom. One read costs two counted queries, so the headroom was one query, and a keyboard that arrives before the field reports focus legitimately makes 19. Skip the pin unless runner.log records input-reports-keyboard-focus, and assert the 17 that route makes. Follow-up to #3906. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(ios): fail the fill pin on a missing focus line and skip multi-poll focus Address the review of #3909. The focus step's log line now carries its poll count, and the log marker and evidence name are shared with the test, so a rename fails the test instead of skipping it. The pin skips unless the first poll confirmed focus, where a second poll adds two queries by design. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Summary
After a keyboard-down fill's focus tap, stabilization asked for the keyboard first. During presentation that query blocks and finds nothing: 0.57–3.77 s in 8 of 12 fills on perf run 39679264668.
Now the tapped input's
ResolvedTextEntryInputanswers first. When it reportshasKeyboardFocus, that read becomes the post-focus read: one snapshot gives focus, the refresh point, and theTextEntryReader's first input. Keyboard and app-wide checks stay as fallbacks;runner.lognames the evidence. Builds on #3882; 5 runner files.Validation
Main
4d5c38498cvsd9065c5ebd(production code of954ce832cd), own iPhone 17 Pro / iOS 27.0 simulator, interleaved, ms median (p95):field-nameMain queried the keyboard after the tap in all 39 keyboard-down fills, clears included; head never.
check:xctest-selectionandcheck:affected --run --head 954ce832cdpassed,exactCommit=true.🤖 Generated with Claude Code