Skip to content

Commit f372eb4

Browse files
javachemeta-codesync[bot]
authored andcommitted
Defer transformed TextInput edits outside UIKit delegate callbacks (#59188)
Summary: Pull Request resolved: #59188 UIKit text change delegate callbacks now remain decision-only when JavaScript transforms an edit. The adapter rejects UIKit's original edit, records an implementation-private transaction, and applies an incremental `UITextInput` replacement on the next main-queue turn after validating the backing-view generation and base text. This preserves partial `maxLength` truncation without synchronously replacing the full attributed string or selection. Marked-text composition remains UIKit-owned; an over-limit committed value is corrected through the same deferred transaction path after composition ends. A runtime gate controls the new path. Stale edits are discarded when the backing text changes, lifecycle and JavaScript-driven updates invalidate queued transactions, and successful commits emit one change update plus the corresponding selection event. Changelog: [iOS][Fixed] - Preserve transformed `TextInput` edits without synchronously rewriting text inside UIKit delegate callbacks Reviewed By: cipolleschi Differential Revision: D126057002
1 parent dd9b6c5 commit f372eb4

26 files changed

Lines changed: 835 additions & 121 deletions

‎packages/react-native/Libraries/Text/React-RCTText.podspec‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Pod::Spec.new do |s|
3434

3535
s.dependency "Yoga"
3636
s.dependency "React-Core/RCTTextHeaders", version
37+
add_dependency(s, "React-featureflags")
3738

3839
mark_as_react_native_build(s)
3940
end

‎packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
#import <React/RCTBackedTextInputDelegateAdapter.h>
1414

15+
@interface RCTBackedTextViewDelegateAdapter (PendingTextInputEdit)
16+
- (void)invalidatePendingTextInputEdit;
17+
- (void)schedulePendingTextInputEditWithRange:(NSRange)range replacementText:(NSString *)replacementText;
18+
@end
19+
1520
@implementation RCTUITextView {
1621
UILabel *_placeholderView;
1722
UITextView *_detachedTextView;
@@ -207,6 +212,16 @@ - (void)setSelectedTextRange:(UITextRange *)selectedTextRange notifyDelegate:(BO
207212
[super setSelectedTextRange:selectedTextRange];
208213
}
209214

215+
- (void)rct_invalidatePendingTextInputEdit
216+
{
217+
[_textInputDelegateAdapter invalidatePendingTextInputEdit];
218+
}
219+
220+
- (void)rct_enqueueTextInputEditWithRange:(NSRange)range replacementText:(NSString *)replacementText
221+
{
222+
[_textInputDelegateAdapter schedulePendingTextInputEditWithRange:range replacementText:replacementText];
223+
}
224+
210225
// After restoring the previous cursor position, we manually trigger the scroll to the new cursor position (PR 38679).
211226
- (void)scrollRangeToVisible:(NSRange)range
212227
{

0 commit comments

Comments
 (0)