Repository navigation
fix(ios): ignore the event emitter attribute when comparing TextInput text - #59718
WataruShirako wants to merge 2 commits into
Conversation
`RCTIsAttributedStringEffectivelySame` decides whether `RCTTextInputComponentView` writes the text JS sent back into the focused field. It compared `RCTAttributedStringEventEmitterKey` like any other attribute, but its value is an NSData holding the address of a heap-allocated weak_ptr, so two wrappers of the same emitter never compare equal, and text typed into the field may not carry the attribute at all. The comparison then reported a difference for identical text and the field was rewritten while the user was typing, which cancels the composition of IMEs that do not use marked text (react#58807) and drops keys typed during the write (react#58904). The attribute is React-internal metadata only read by Paragraph hit testing; a TextInput never dispatches through it, so it is now ignored. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Hi @WataruShirako! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
…comment Two wrappers of the same emitter do compare equal (the NSData holds the pointee address), so the earlier wording was wrong; the mismatch is the attribute present on the state's text and absent from the typed text, which the test without the fix now shows. The second test uses two distinct emitters to document that even a different emitter is not a difference for a TextInput. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary:
Related to #58807 and #58904; part of the CJK IME problems collected in #56463 / #48497.
-[RCTTextInputComponentView _textOf:equals:]callsRCTIsAttributedStringEffectivelySameto decide whether the text JS sent back inupdateState:must be written into the field withsetAttributedText:. While the user is typing, that write resets UIKit's input pipeline: it cancels the composition of IMEs that do not use marked text (Simeji in #58807, where the field has nomarkedTextRangeso the bare-text fallback does not apply) and drops the keys in flight (#58904).The comparison treated
RCTAttributedStringEventEmitterKeylike any other attribute. The text a state is built from carries it on every fragment (RCTNSAttributedStringFromAttributedStringadds the parent shadow view's emitter), while text typed into the field does not always carry it (UIKit builds typed text from the field's typing attributes, which are not guaranteed to keep a custom attribute across edits). The reporter's native instrumentation in #58807 shows exactly this (key=EventEmitter incomingRaw=<present> currentRaw=(null)) right before the composition is interrupted. Present on one side and absent on the other, the attribute makes identical text compare different, and the field is rewritten.The attribute is React-internal metadata: its only reader is
RCTTextLayoutManager's hit testing for<Text>press events, and aTextInputdispatches its events through its own emitter, not through the attribute.RCTIsAttributedStringEffectivelySamehas a single caller (_textOf:equals:), so this change only affects whether a TextInput rewrites text it already shows. The attribute is now ignored in the comparison, whether it differs or is missing on one side.Scope relative to the other open work on this path:
defaultTextAttributes(which include the emitter) as insensitive attributes, which would cover the missing-emitter case as part of a larger change. This PR is the minimal fix for the emitter alone; if fix(ios): don't write a TextInput's own text back into the focused field (#58904) #59197 lands first this hunk becomes redundant, if this lands first fix(ios): don't write a TextInput's own text back into the focused field (#58904) #59197 is still needed for paragraph styles.typingAttributesas part of a larger change; comparing without it fixes the mismatch regardless of which side carries it.Changelog:
[IOS] [FIXED] - Stop rewriting a focused TextInput's text because of the internal event emitter attribute, which interrupted IME composition and dropped keys
Test Plan:
Added three cases to
RCTAttributedTextUtilsTest:testSamenessOfMissingEventEmitter: the attribute present on one side only is not a difference (the [iOS] TextInput interrupts Japanese composition with the Simeji keyboard #58807 case)testSamenessOfDifferentEventEmitters: two different emitters are not a difference eithertestDifferenceOfOtherAttributesNextToEventEmitter: a real attribute difference in the same run is still reportedReact/Testsis not compiled by any open source target, so I added the file toRNTesterUnitTestslocally (not part of this change) and ran it on an iPhone 17 Pro simulator, iOS 26.5, with RNTester built from source (RCT_USE_PREBUILT_RNCORE=0).maincurrently has an unrelated compile failure inRNTesterUnitTests/RCTImageLoaderTests.m(RCTDefineImageURLLoader/RCTDefineImageDecoderare no longer defined anywhere in the tree), so that file was excluded from the local target for these runs.Executed 27 tests, with 4 failures:testSamenessOfMissingEventEmitterandtestSamenessOfDifferentEventEmittersfail (both assertions each);testDifferenceOfOtherAttributesNextToEventEmitterand the 24 existing tests passExecuted 27 tests, with 0 failures,** TEST SUCCEEDED **Manual verification in RNTester (Debug, new architecture, same simulator, hardware keyboard disconnected so the software IME is used), with this Playground: a controlled single-line
TextInput, a controlled multiline one, a controlled one withmaxLength={5}and an uncontrolled one, plus a counter ofonChangeTextcalls.valuelabel and the counter follow the inputmaxLength={5}:日本語commits fine;Hello worldtyped fast is cut toHellozhunderlined while composing,中国chosen from the candidatesHello worldtyped quickly in the controlled single-line input, no key droppedRecording (iOS 26.5, iPhone 17 Pro simulator, with the fix): ime-after-ios26.5-sim.mp4 (branch
evidence/ios-ime-event-emitteron my fork).Simeji cannot be installed on a simulator, so the #58807 scenario itself (an IME that does not use marked text, with the event emitter missing from the typed text) is covered by the unit test and by the reporter's instrumentation rather than by this recording. I will ask the reporter to confirm on a device.
RNTesterPlayground.js used for the recording