fix(mobile): the iPhone search X clears Threads and Settings#30399

closed4
main
GitHub fork: issue-watch/29489
opened9d agobySshivam
View on GitHubTwo-way sync enabledPR last imported 3d ago

Problem

On iOS 27, the round X beside integrated search closes it without telling JavaScript, so Threads and Settings stay filtered by the old query. iOS 26 reports the X as a cancel itself. Fixes pingdotgg/t3code#29489.

Change

  • Screens 5.0.0-t3.49, packed from pingdotgg/react-native-screens@5d02fdd0 (fork PR pingdotgg/react-native-screens#9). It builds on shivamhwp's dismissal delegate, which grew out of GLDRoger's pingdotgg/react-native-screens#8: a user dismissal that leaves the field empty goes through the existing cancel path. Navigation, setActive(false), detached headers and cancels already reported are excluded.
  • Synthetic cancel only on iOS 27 and later. iOS 26 already sends its own cancel for the X. On 26.5, switching tabs also dismisses search with an empty field, so the ungated synthetic cancel cleared the saved query: keyboard Search → Usage/PRs → back lost the query in 5 of 5 runs, while unmodified t3.48 kept it. That applied to both #29527 and this PR's previous head.
  • Toolbar (shivamhwp). The saved query is written back into the field only when the search bar appears or moves, not after a blur. Otherwise the X's blur put the query back into the closing field. This also stops the text being echoed back while typing.
  • Same base as t3.48. The tarball is built on t3/release-t3.48 (6ce484b), so the shipped Android sheet fixes stay. Compared with t3.48, only RNSSearchBar.h/.mm, package.json and t3-fork.json differ, and 838 packaged sources match the fork commit byte for byte. t3-fork.json and pack-t3.mjs now name pingdotgg/react-native-screens.

Verification

Simulators: iPhone 18 Pro on iOS 27.0, iPhone 17 on iOS 26.5, iPad Pro 13" on iOS 27.0, and a Pixel 10 Pro Fold emulator on Android 17. Each ran a native client built from this head (fingerprint 30c2bab4). Temporary logging on the search handlers recorded every event.

CheckiOS 27.0iOS 26.5iPad, iOS 27.0
(a) X clears Threads✅ 3/3✅ 3/3n/a, no X; in-field clear ✅
(a) X clears Settings✅ 2/2✅ (unchanged UIKit cancel)–
(b) keyboard Search → Usage/PRs → back keeps query✅ 5/5✅ 5/5, Settings too✅ 2/2
(c) open a matching thread → back keeps query (#24748)✅✅✅
(d) one cancel per X✅ (synthetic: blur → cancel → empty text)✅ (UIKit's own)✅
(e) cold launch → keyboard Search → first Usage crash1 confirmed crash in 10 attempts, plus 5 warm switches with none0 in 10 cold + 5 warm–

Android: a routed sheet ("Linked pull requests") on the unfolded Fold is still centered at the Material form-sheet width.

Captures of Threads are left out because they show real thread titles. vp run check:changed passes: format, lint, knip, 41 typechecks, the toolbar test and React Compiler.

Usage crash: already on main

The crash this PR's earlier description reported reproduced on iOS 27.0. It is RCTAssert(allowsEmptyStack || viewControllers.count > 0) in -[RNSStackNavigationController performContainerUpdateIfNeeded], during a mount transaction. It also reproduced on unmodified main (Screens t3.48) in the same cold-launch → Search → Usage loop: 1 confirmed crash in 8 attempts. The two crash reports come from different binaries with the same stack. So this change doesn't cause it, and it needs its own issue. Taking this PR neither adds nor removes that risk.

Landing order

  1. Merge pingdotgg/react-native-screens#9 into t3/main. It also brings t3/main up to t3.48's Android fixes.
  2. On pingdotgg/react-native-screens, push tag t3-v5.0.0-t3.49 and branch t3/release-t3.49 at 5d02fdd0. The docs link in this PR points at that tag.
  3. Merge this PR.

Not verified: iOS 27.2, a physical device, iPhone Duo, and PR-list search (no PR data in the test environment).

Co-authored by shivamhwp and GLDRoger. Rebased, re-gated and verified by Claude Opus 5.5 (Claude Code) in T3 Code.

🤖 Generated with Claude Code

Checks

23 successful · 12 skipped
All checks passed
fb1aec7

[GPT-6.1 Sol][Codex] on behalf of shivam.

@juliusmarminge @t3dotgg, the bounded Usage-crash comparison is complete. Please record a disposition for the unresolved crash risk at app be3b48c10e39a045eea29b825cf368f886a6799d and companion native PR https://github.com/pingdotgg/react-native-screens/pull/9 at 11f8d62302bac4c16e627862b3ef7066587e81e4.

The search fix passes its shipping-client X and query-retention cases, but one initial first-Usage transition hit the native final-empty-stack assertion. The subsequent four diagnostic processes without LLDB all passed. Their traces show successful first-root installation or reuse and suppression of detached search cancellation, with no dropped events. The updated body has exact builds, deviations, operation excerpts and three new clips. A fresh independent evidence review found no new confirmed defect or evidence-backed next automatic crash experiment. Logging changes timing; the original failure remains unattributed, and baseline occurrence is unknown.

I recommend accepting this residual risk for the search change with a separately tracked navigation investigation, then completing the outstanding native fixture and remaining verification before marking either PR ready. The alternative is to keep both drafts until that investigation produces a failing reproducer and fix. Which disposition do you want? Both remain draft; no further no-crash retries or speculative navigation edits are planned.

Added 4 commits

  • fix(mobile): the iPhone search X clears Threads and Settings search0b4b894
  • chore(mobile): repack react-native-screens t3.49 with fork provenance3c4f27a
  • fix(mobile): a blur no longer writes the Threads query back into closing search60628db
  • fix(mobile): only report the search X as a cancel on iOS 27fb1aec7

[GPT-6.1 Sol][Codex] on behalf of shivam.

Julius pushed his own commit 5d02fdd0a10edfb94bb9173607b997a0e5e78401 to the native companion, https://github.com/pingdotgg/react-native-screens/pull/9. Per fleet's takeover rule, I am stopping automated changes on this stack and handing it over.

The app is still at 1f474a926c59a77d24dd1b0db2e0a3fef20925f6, packaging native source e7a4b766d6d6f80c6ea26a605c9bceaf7f840d10; it does not yet package the new iOS 27 gate. Its CI, changed-file checks and 25 focused tests passed. Runtime media in the body is historical; the original Usage assertion and native Detox case remain unresolved/unrun. The native PR's current two check entries are skipped draft reviews, with no new CodeRabbit finding.

The final iPhone run dispatched before the takeover has now completed. Both pinned clients compiled, but installation failed with install requires an active session or an explicit device selector; the follow-up help command also rejected --session. No app was installed and no iPhone case ran, so this adds no runtime proof. I inspected the completed run's cleanup output: the simulator was closed, the lock released, the backend stopped, and both worktrees removed. Its remaining report/artifact folder could not be collected or deleted because SSH still times out. The completed test thread is archived. No replacement test was started, and both PR watches are removed.

Thanks for the handoff. The app now packages the iOS 27 gate: head fb1aec7f is rebased onto main and repacks 5.0.0-t3.49 from pingdotgg/react-native-screens@5d02fdd0.

On the open crash question: the native final-empty-stack assertion (performContainerUpdateIfNeeded) also reproduces on unmodified main with Screens t3.48. In the same loop on an iOS 27.0 simulator (cold launch → keyboard Search → first Usage), it crashed once in 8 attempts on main and once in 10 on this head. The two crash reports come from different binaries with the same stack. On iOS 26.5 this head had 0 crashes in 10 cold attempts. So the risk already exists on main and isn't added by this change. It should get its own issue rather than holding the search fix. The decision on whether to wait for that is still Julius's, so this PR stays a draft.

The results table, the reason for the iOS 27 gate and the landing order are in the updated description. The previous runtime media has been replaced.

Closing this. On iOS 27.2 the integrated-search X already clears the query and the filter on Threads and Settings with main's Screens t3.48 (checked on a device), so UIKit appears to report the cancel itself again. All testing here was on 27.0, and adding a synthetic cancel on every 27.x for a bug that seems fixed upstream isn't worth a native fork change. Thanks @shivamhwp and @GLDRoger for the work; if 27.0/27.1 turns out to matter, this can come back gated to those versions only.