Fix possible NSRangeException when updating typing attributes in response to new text content (#47737)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47737

We may see an NSRangeException when setting new AttributedString content, where setting the AttributedString itself changes selection (before we mutate it later).

It seems like the selection here is not in a good state yet in regards to the AttributedString backing exposed (since we are reading it while modifying it). So let's fold the logic for updating typing attributes into the collection of ignored work from non-user-selection updates, since programatically setting an AttributedString will already trigger updating typing attributes.

I also added a nil check here, which is unrelated to the crash, but it seems like we should have it for safety...

Changelog:
[iOS][Fixed] - Fix possible NSRangeException when updating typing attributes in response to new text content

Reviewed By: cipolleschi

Differential Revision: D66202986

fbshipit-source-id: fded492b5022c5fef5b9563f93a57549d06a7020
This commit is contained in:
Nick Gerleman 2024-11-20 14:05:33 -08:00 committed by Facebook GitHub Bot
parent 48ea6867a9
commit 6e06a810f0

View File

@ -450,10 +450,15 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
- (void)textInputDidChangeSelection - (void)textInputDidChangeSelection
{ {
[self _updateTypingAttributes];
if (_comingFromJS) { if (_comingFromJS) {
return; return;
} }
// T207198334: Setting a new AttributedString (_comingFromJS) will trigger a selection change before the backing
// string is updated, so indicies won't point to what we want yet. Only respond to user selection change, and let
// `_setAttributedString` handle updating typing attributes if content changes.
[self _updateTypingAttributes];
const auto &props = static_cast<const TextInputProps &>(*_props); const auto &props = static_cast<const TextInputProps &>(*_props);
if (props.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) { if (props.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
[self textInputDidChange]; [self textInputDidChange];
@ -722,7 +727,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
// https://github.com/facebook/react-native/blob/3102a58df38d96f3dacef0530e4dbb399037fcd2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/SetSpanOperation.kt#L30 // https://github.com/facebook/react-native/blob/3102a58df38d96f3dacef0530e4dbb399037fcd2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/SetSpanOperation.kt#L30
- (void)_updateTypingAttributes - (void)_updateTypingAttributes
{ {
if (_backedTextInputView.attributedText.length > 0) { if (_backedTextInputView.attributedText.length > 0 && _backedTextInputView.selectedTextRange != nil) {
NSUInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument NSUInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
toPosition:_backedTextInputView.selectedTextRange.start]; toPosition:_backedTextInputView.selectedTextRange.start];