Files
orca/mobile/patches/react-native@0.83.10.patch
T
NeilandShahar Mor 45ee56e1a5 fix(mobile): preserve native iOS dictation placeholders (#31356)
Preserve UIKit recognition placeholders during equal-text Fabric synchronization and reset recognition state when the native input is recycled.

Adapted from community PR #26644 by Shahar Mor. Verified with matched source-built native Debug lifecycle probes for both input classes; physical spoken dictation remains in the final manual batch.

Co-authored-by: Shahar Mor <shaharmor1@gmail.com>
2026-10-20 23:52:49 -07:00

117 lines
5.3 KiB
Diff

diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
index d02e003a439e410ddab597b2c036256c80db0395..cf745cf37a37d34f9e93eb8df10b43cb54ab91a3 100644
--- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
+++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
@@ -382,6 +382,7 @@
_backedTextInputView.inputAccessoryView = nil;
_hasInputAccessoryView = false;
[_backedTextInputView resignFirstResponder];
+ [_backedTextInputView resetDictationRecognition];
}
#pragma mark - RCTBackedTextInputDelegate
@@ -703,6 +704,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
{
return {
.text = RCTStringFromNSString(_backedTextInputView.attributedText.string),
+ .isComposing = _backedTextInputView.markedTextRange != nil,
.selectionRange = [self _selectionRange],
.eventCount = static_cast<int>(_mostRecentEventCount),
.contentOffset = RCTPointFromCGPoint(_backedTextInputView.contentOffset),
@@ -865,4 +867,6 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
+ // Preserve the native recognition placeholder while iOS finalizes dictation.
BOOL shouldFallbackToBareTextComparison =
[_backedTextInputView.textInputMode.primaryLanguage isEqualToString:@"dictation"] ||
+ _backedTextInputView.dictationRecognizing ||
[_backedTextInputView.textInputMode.primaryLanguage isEqualToString:@"ko-KR"] ||
_backedTextInputView.markedTextRange || _backedTextInputView.isSecureTextEntry || fontHasBeenUpdatedBySystem;
diff --git a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp
index a9bc219f8bf729111e907423ca61b991fd712b46..60e9c6f54edb295295d6e48d1d554499106b7e57 100644
--- a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp
+++ b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp
@@ -24,6 +24,10 @@ static jsi::Value textInputMetricsPayload(
payload.setProperty(runtime, "eventCount", textInputMetrics.eventCount);
+ if (textInputMetrics.isComposing.has_value()) {
+ payload.setProperty(runtime, "isComposing", *textInputMetrics.isComposing);
+ }
+
if (includeSelectionState) {
auto selection = jsi::Object(runtime);
selection.setProperty(
diff --git a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h
index a3f2e01c130c1a53aee0b39e72927d21f2a6973d..c9d34f8b42644800b5c85e5be45b6ebfbe94a54d 100644
--- a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h
+++ b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h
@@ -7,6 +7,8 @@
#pragma once
+#include <optional>
+
#include <react/renderer/attributedstring/AttributedString.h>
#include <react/renderer/components/view/ViewEventEmitter.h>
@@ -18,6 +20,7 @@ class TextInputEventEmitter : public ViewEventEmitter {
struct Metrics {
std::string text;
+ std::optional<bool> isComposing;
AttributedString::Range selectionRange;
// ScrollView-like metrics
Size contentSize;
diff --git a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
index 79ee7ffe43..956f189a1c 100644
--- a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
+++ b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
@@ -136,6 +136,7 @@ using namespace facebook::react;
- (void)prepareForRecycle
{
[super prepareForRecycle];
_textView.state = nullptr;
+ _textView.layoutMetrics = EmptyLayoutMetrics;
_accessibilityProvider = nil;
}
diff --git a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
--- a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
+++ b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
@@ -48,6 +48,7 @@
- (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange NS_UNAVAILABLE;
- (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange notifyDelegate:(BOOL)notifyDelegate;
- (void)scrollRangeToVisible:(NSRange)selectedTextRange;
+- (void)resetDictationRecognition;
// This protocol disallows direct access to `text` property because
// unwise usage of it can break the `attributeText` behavior.
diff --git a/Libraries/Text/TextInput/Singleline/RCTUITextField.mm b/Libraries/Text/TextInput/Singleline/RCTUITextField.mm
--- a/Libraries/Text/TextInput/Singleline/RCTUITextField.mm
+++ b/Libraries/Text/TextInput/Singleline/RCTUITextField.mm
@@ -197,6 +197,11 @@
}
#pragma mark - Dictation
+
+- (void)resetDictationRecognition
+{
+ _dictationRecognizing = NO;
+}
- (void)dictationRecordingDidEnd
{
diff --git a/Libraries/Text/TextInput/Multiline/RCTUITextView.mm b/Libraries/Text/TextInput/Multiline/RCTUITextView.mm
--- a/Libraries/Text/TextInput/Multiline/RCTUITextView.mm
+++ b/Libraries/Text/TextInput/Multiline/RCTUITextView.mm
@@ -319,6 +319,11 @@
#pragma mark - Dictation
+- (void)resetDictationRecognition
+{
+ _dictationRecognizing = NO;
+}
+
- (void)dictationRecordingDidEnd
{
_dictationRecognizing = YES;