# fix\(mobile\): a closed search no longer risks freezing the app · PR \#23148 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/23148)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: duo/search-observer

Target branch: duo/search

Head commit: fdfc6ea1c154022451eda16aed4ef66b5d19b610

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/23148)

[Commits](https://git.cafe/pingdotgg/t3code/pull/23148/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/23148/diffs?format=markdown)

## Description

---

UIKit's search field leaves a main-queue observer on its text storage after the field is freed: `_UISearchBarTextFieldTokenCounter` registers for `NSTextStorageDidProcessEditingNotification`, filtered by the storage's pointer. When React Native's JS thread later measures text in a new storage at the same address, the edit waits for the main thread while holding React Native's text measurement lock. A fold that measures text on the main thread then deadlocks. Closing any screen with a search, or re-placing a search on a fold, left one behind. This was reproduced on the Duo simulator with no XCTest loaded.

- Screens 5.0.0-t3.33 removes those observers when their storage is freed.
- Screens 5.0.0-t3.34 narrows that to UIKit's own search field observer, identified by the block function UIKit registers while the first search controller is created, and holds it weakly.

> [!WARNING]
> **Needs a deliberate review.** To recognize UIKit's registration, the fork replaces `-[NSNotificationCenter addObserverForName:object:queue:usingBlock:]` process-wide (from `+[RNSSearchBar initialize]`), and it relies on the shape of a private UIKit block. Treat it as a workaround to remove once UIKit or React Native fixes the underlying issue, not as a final fix. Sources: [t3.33](https://github.com/pingdotgg/react-native-screens/tree/t3-v5.0.0-t3.33) and [t3.34](https://github.com/pingdotgg/react-native-screens/tree/t3-v5.0.0-t3.34), in [pingdotgg/react-native-screens#3](https://github.com/pingdotgg/react-native-screens/pull/3).

## Verification

lldb on the t3.34 dev client counted the remover's work across opening and closing searches and folding:
- 3 UIKit token counters were created, 3 removers attached, and 3 observers were removed.
- A third-party text storage observer, registered as a control, kept firing and was never touched.

There are no screenshots because the change has no visual effect.

Implemented with Claude Opus 5.5 in Claude Code, running in T3 Code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

