Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 85e7c5d

Browse files
authored
Add Search Web to selection controls on iOS (#43324)
In native iOS, users are able to select text and initiate a web search on the selected text. Specifically, this will launch a search using the users selected default search engine. Apple provides a custom url scheme in the form of "x-web-search://?[term]" which will automatically launch a search using the user's preferred browser in Safari. Additionally, this also correctly handles the country code top level domain eg. a user with a selected region in the UK with a preferred Google search engine will automatically have the .co.uk domain appended to the end of the search. This PR is the engine portion of the changes that will allow Search Web to be implemented This PR addresses flutter/flutter#82907 More details are available in this [design doc](https://github.com/flutter/engine/pull/flutter.dev/go/add-missing-features-to-selection-controls)
1 parent f0ce6b6 commit 85e7c5d

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
constexpr char kTextPlainFormat[] = "text/plain";
1818
const UInt32 kKeyPressClickSoundId = 1306;
19+
const NSString* searchURLPrefix = @"x-web-search://?";
1920

2021
} // namespace
2122

@@ -115,6 +116,9 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
115116
result([self clipboardHasStrings]);
116117
} else if ([method isEqualToString:@"LiveText.isLiveTextInputAvailable"]) {
117118
result(@([self isLiveTextInputAvailable]));
119+
} else if ([method isEqualToString:@"SearchWeb.invoke"]) {
120+
[self searchWeb:args];
121+
result(nil);
118122
} else if ([method isEqualToString:@"LookUp.invoke"]) {
119123
[self showLookUpViewController:args];
120124
result(nil);
@@ -123,6 +127,17 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
123127
}
124128
}
125129

130+
- (void)searchWeb:(NSString*)searchTerm {
131+
NSString* escapedText = [searchTerm
132+
stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet
133+
URLHostAllowedCharacterSet]];
134+
NSString* searchURL = [NSString stringWithFormat:@"%@%@", searchURLPrefix, escapedText];
135+
136+
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:searchURL]
137+
options:@{}
138+
completionHandler:nil];
139+
}
140+
126141
- (void)playSystemSound:(NSString*)soundType {
127142
if ([soundType isEqualToString:@"SystemSoundType.click"]) {
128143
// All feedback types are specific to Android and are treated as equal on

shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ @interface FlutterPlatformPluginTest : XCTestCase
1717

1818
@interface FlutterPlatformPlugin ()
1919
- (BOOL)isLiveTextInputAvailable;
20+
- (void)searchWeb:(NSString*)searchTerm;
2021
- (void)showLookUpViewController:(NSString*)term;
2122
@end
2223

@@ -27,6 +28,30 @@ - (void)presentViewController:(UIViewController*)viewControllerToPresent
2728
@end
2829

2930
@implementation FlutterPlatformPluginTest
31+
- (void)testSearchWebInvoked {
32+
FlutterEngine* engine = [[[FlutterEngine alloc] initWithName:@"test" project:nil] autorelease];
33+
std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory =
34+
std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(engine);
35+
[engine runWithEntrypoint:nil];
36+
37+
XCTestExpectation* invokeExpectation =
38+
[self expectationWithDescription:@"Web search launched with search term"];
39+
40+
FlutterPlatformPlugin* plugin =
41+
[[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()] autorelease];
42+
FlutterPlatformPlugin* mockPlugin = OCMPartialMock(plugin);
43+
44+
FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"SearchWeb.invoke"
45+
arguments:@"Test"];
46+
47+
FlutterResult result = ^(id result) {
48+
OCMVerify([mockPlugin searchWeb:@"Test"]);
49+
[invokeExpectation fulfill];
50+
};
51+
52+
[mockPlugin handleMethodCall:methodCall result:result];
53+
[self waitForExpectationsWithTimeout:1 handler:nil];
54+
}
3055

3156
- (void)testLookUpCallInitiated {
3257
FlutterEngine* engine = [[[FlutterEngine alloc] initWithName:@"test" project:nil] autorelease];

0 commit comments

Comments
 (0)