diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 71f5354c4b64d..507b9cae8434a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -537,10 +537,7 @@ - (void)setTextInputClient:(int)client { _textInputClient = client; } -// Return true if the new input state needs to be synced back to the framework. -// TODO(LongCatIsLooong): setTextInputState should never call updateEditingState. Sending the -// editing value back may overwrite the framework's updated editing value. -- (BOOL)setTextInputState:(NSDictionary*)state { +- (void)setTextInputState:(NSDictionary*)state { NSString* newText = state[@"text"]; BOOL textChanged = ![self.text isEqualToString:newText]; if (textChanged) { @@ -575,9 +572,6 @@ - (BOOL)setTextInputState:(NSDictionary*)state { if (textChanged) { [self.inputDelegate textDidChange:self]; } - - // For consistency with Android behavior, send an update to the framework if the text changed. - return textChanged; } // Extracts the selection information from the editing state dictionary. @@ -1423,9 +1417,7 @@ - (void)addToInputParentViewIfNeeded:(FlutterTextInputView*)inputView { } - (void)setTextInputEditingState:(NSDictionary*)state { - if ([_activeView setTextInputState:state]) { - [_activeView updateEditingState]; - } + [_activeView setTextInputState:state]; } - (void)clearTextInputClient { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m index 432ce718bf31a..88683abb087f6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m @@ -16,7 +16,7 @@ @interface FlutterTextInputView () @property(nonatomic, copy) NSString* autofillId; - (void)setEditableTransform:(NSArray*)matrix; -- (BOOL)setTextInputState:(NSDictionary*)state; +- (void)setTextInputState:(NSDictionary*)state; - (void)setMarkedRect:(CGRect)markedRect; - (void)updateEditingState; - (BOOL)isVisibleToAutofill; @@ -211,71 +211,45 @@ - (void)testUITextInputCallsUpdateEditingStateOnce { XCTAssertEqual(updateCount, 6); } -- (void)testTextChangesTriggerUpdateEditingClient { +- (void)testTextChangesDoNotTriggerUpdateEditingClient { FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init]; inputView.textInputDelegate = engine; - [inputView.text setString:@"BEFORE"]; - inputView.markedTextRange = nil; - inputView.selectedTextRange = nil; - - // Text changes trigger update. - XCTAssertTrue([inputView setTextInputState:@{@"text" : @"AFTER"}]); - - // Don't send anything if there's nothing new. - XCTAssertFalse([inputView setTextInputState:@{@"text" : @"AFTER"}]); -} + __block int updateCount = 0; + OCMStub([engine updateEditingClient:0 withState:[OCMArg isNotNil]]) + .andDo(^(NSInvocation* invocation) { + updateCount++; + }); -- (void)testSelectionChangeDoesNotTriggerUpdateEditingClient { - FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init]; - inputView.textInputDelegate = engine; + [inputView.text setString:@"BEFORE"]; + XCTAssertEqual(updateCount, 0); - [inputView.text setString:@"SELECTION"]; inputView.markedTextRange = nil; inputView.selectedTextRange = nil; + XCTAssertEqual(updateCount, 1); - BOOL shouldUpdate = [inputView - setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @0, @"selectionExtent" : @3}]; - XCTAssertFalse(shouldUpdate); + // Text changes don't trigger an update. + XCTAssertEqual(updateCount, 1); + [inputView setTextInputState:@{@"text" : @"AFTER"}]; + XCTAssertEqual(updateCount, 1); + [inputView setTextInputState:@{@"text" : @"AFTER"}]; + XCTAssertEqual(updateCount, 1); - shouldUpdate = [inputView + // Selection changes don't trigger an update. + [inputView + setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @0, @"selectionExtent" : @3}]; + XCTAssertEqual(updateCount, 1); + [inputView setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @3}]; - XCTAssertFalse(shouldUpdate); - - shouldUpdate = [inputView - setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @2}]; - XCTAssertFalse(shouldUpdate); - - // Don't send anything if there's nothing new. - shouldUpdate = [inputView - setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @2}]; - XCTAssertFalse(shouldUpdate); -} - -- (void)testComposingChangeDoesNotTriggerUpdateEditingClient { - FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init]; - inputView.textInputDelegate = engine; - - // Reset to test marked text. - [inputView.text setString:@"COMPOSING"]; - inputView.markedTextRange = nil; - inputView.selectedTextRange = nil; - - BOOL shouldUpdate = [inputView - setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @0, @"composingExtent" : @3}]; - XCTAssertFalse(shouldUpdate); - - shouldUpdate = [inputView - setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @3}]; - XCTAssertFalse(shouldUpdate); - - shouldUpdate = [inputView - setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @2}]; - XCTAssertFalse(shouldUpdate); + XCTAssertEqual(updateCount, 1); - shouldUpdate = [inputView + // Composing region changes don't trigger an update. + [inputView setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @2}]; - XCTAssertFalse(shouldUpdate); + XCTAssertEqual(updateCount, 1); + [inputView + setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @3}]; + XCTAssertEqual(updateCount, 1); } - (void)testUITextInputAvoidUnnecessaryUndateEditingClientCalls {