diff --git a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm index e4a1420c10e36..8ea26cc258063 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm @@ -66,17 +66,18 @@ - (NSData*)encode:(id)message { return nil; } NSData* encoding; + NSError* error; if ([message isKindOfClass:[NSArray class]] || [message isKindOfClass:[NSDictionary class]]) { - encoding = [NSJSONSerialization dataWithJSONObject:message options:0 error:nil]; + encoding = [NSJSONSerialization dataWithJSONObject:message options:0 error:&error]; } else { // NSJSONSerialization does not support top-level simple values. // We encode as singleton array, then extract the relevant bytes. - encoding = [NSJSONSerialization dataWithJSONObject:@[ message ] options:0 error:nil]; + encoding = [NSJSONSerialization dataWithJSONObject:@[ message ] options:0 error:&error]; if (encoding) { encoding = [encoding subdataWithRange:NSMakeRange(1, encoding.length - 2)]; } } - NSAssert(encoding, @"Invalid JSON message, encoding failed"); + NSAssert(encoding, @"Invalid JSON message, encoding failed: %@", error); return encoding; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 70ac5d1d784e7..6b0e9af3c0cb4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -707,6 +707,7 @@ - (void)forwardInvocation:(NSInvocation*)anInvocation { @interface FlutterTextInputPlugin () @property(nonatomic, readonly) fml::WeakPtr weakPtr; @property(nonatomic, readonly) id textInputDelegate; +@property(nonatomic, readonly) UIView* hostView; @end @interface FlutterTextInputView () @@ -1602,7 +1603,10 @@ - (CGRect)firstRectForRange:(UITextRange*)range { _cachedFirstRect = [self localRectFromFrameworkTransform:rect]; } - return _cachedFirstRect; + UIView* hostView = _textInputPlugin.get().hostView; + NSAssert(hostView == nil || [self isDescendantOfView:hostView], @"%@ is not a descendant of %@", + self, hostView); + return hostView ? [hostView convertRect:_cachedFirstRect toView:self] : _cachedFirstRect; } if (_scribbleInteractionStatus == FlutterScribbleInteractionStatusNone && diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm index 2e0198bafec3a..c83bd15640d99 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm @@ -1217,7 +1217,11 @@ - (void)testInputViewsDoNotHaveUITextInteractions { #pragma mark - UITextInput methods - Tests - (void)testUpdateFirstRectForRange { - FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithOwner:textInputPlugin]; + [self setClientId:123 configuration:self.mutableTemplateCopy]; + + FlutterTextInputView* inputView = textInputPlugin.activeView; + textInputPlugin.viewController.view.frame = CGRectMake(0, 0, 0, 0); + [inputView setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @3}]; @@ -1272,6 +1276,16 @@ - (void)testUpdateFirstRectForRange { [inputView setMarkedRect:testRect]; XCTAssertTrue( CGRectEqualToRect(CGRectMake(-306, 3, 300, 300), [inputView firstRectForRange:range])); + + NSAssert(inputView.superview, @"inputView is not in the view hierarchy!"); + const CGPoint offset = CGPointMake(113, 119); + CGRect currentFrame = inputView.frame; + currentFrame.origin = offset; + inputView.frame = currentFrame; + // Moving the input view within the FlutterView shouldn't affect the coordinates, + // since the framework sends us global coordinates. + XCTAssertTrue(CGRectEqualToRect(CGRectMake(-306 - 113, 3 - 119, 300, 300), + [inputView firstRectForRange:range])); } - (void)testFirstRectForRangeReturnsCorrectSelectionRect {