From 96c58acd227cc2b020fe0f798239e936075214d5 Mon Sep 17 00:00:00 2001 From: Alban DEFLANDRE Date: Thu, 22 Apr 2021 16:57:50 +0200 Subject: [PATCH 1/4] Handle iOS Universal Links --- .../framework/Source/FlutterAppDelegate.mm | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index 472a6e8b1fd44..37851be82d988 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -227,9 +227,43 @@ - (BOOL)application:(UIApplication*)application continueUserActivity:(NSUserActivity*)userActivity restorationHandler:(void (^)(NSArray* __nullable restorableObjects))restorationHandler { #endif - return [_lifeCycleDelegate application:application - continueUserActivity:userActivity - restorationHandler:restorationHandler]; + if ([_lifeCycleDelegate application:application + continueUserActivity:userActivity + restorationHandler:restorationHandler]) { + return YES; + } else if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { + return NO; + } else { + NSURLComponents* components = [NSURLComponents componentsWithURL:userActivity.webpageURL + resolvingAgainstBaseURL:YES]; + + if (components == nil or components.path == nil) { + return NO; + } + FlutterViewController* flutterViewController = [self rootFlutterViewController]; + if (flutterViewController) { + [flutterViewController.engine + waitForFirstFrame:3.0 + callback:^(BOOL didTimeout) { + if (didTimeout) { + FML_LOG(ERROR) << "Timeout waiting for the first frame when launching a " + "universal link."; + } else { + NSString* pathAndQuery = components.path; + if (components.query != nil and [components.query length] != 0) { + pathAndQuery = + [NSString stringWithFormat:@"%@?%@", pathAndQuery, components.query]; + } + [flutterViewController.engine.navigationChannel invokeMethod:@"pushRoute" + arguments:pathAndQuery]; + } + }]; + return YES; + } else { + FML_LOG(ERROR) << "Attempting to open a universal link without a Flutter RootViewController."; + return NO; + } + } } #pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController From 6da79a77c9f1ddb3559dd63084653bc576ba2518 Mon Sep 17 00:00:00 2001 From: Alban DEFLANDRE Date: Thu, 22 Apr 2021 17:57:40 +0200 Subject: [PATCH 2/4] Add iOS universal link test case --- .../Source/FlutterAppDelegateTest.mm | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm index 6a62697dbfe3f..0af67b23930df 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm @@ -42,4 +42,26 @@ - (void)skip_testLaunchUrl { OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test"]); } +- (void)skip_testLaunchUniversalLink { + FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init]; + FlutterViewController* viewController = OCMClassMock([FlutterViewController class]); + FlutterEngine* engine = OCMClassMock([FlutterEngine class]); + FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]); + OCMStub([engine navigationChannel]).andReturn(navigationChannel); + OCMStub([viewController engine]).andReturn(engine); + OCMStub([engine waitForFirstFrame:3.0 callback:([OCMArg invokeBlockWithArgs:@(NO), nil])]); + appDelegate.rootFlutterViewControllerGetter = ^{ + return viewController; + }; + NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; + NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb]; + userActivity.webpageURL = url; + BOOL result = [appDelegate application:[UIApplication sharedApplication] + continueUserActivity:userActivity + restorationHandler: ^(NSArray>* __nullable + restorableObjects){}]; + XCTAssertTrue(result); + OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test"]); +} + @end From 7fdc6e7f5357e630e34bc5c6c5c6284a6ff1ce11 Mon Sep 17 00:00:00 2001 From: Alban DEFLANDRE Date: Thu, 22 Apr 2021 18:05:47 +0200 Subject: [PATCH 3/4] Format universal link test case --- .../ios/framework/Source/FlutterAppDelegateTest.mm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm index 0af67b23930df..ff1fba8202d99 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm @@ -54,12 +54,14 @@ - (void)skip_testLaunchUniversalLink { return viewController; }; NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; - NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb]; + NSUserActivity* userActivity = + [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb]; userActivity.webpageURL = url; - BOOL result = [appDelegate application:[UIApplication sharedApplication] - continueUserActivity:userActivity - restorationHandler: ^(NSArray>* __nullable - restorableObjects){}]; + BOOL result = [appDelegate + application:[UIApplication sharedApplication] + continueUserActivity:userActivity + restorationHandler: ^(NSArray>* __nullable restorableObjects){ + }]; XCTAssertTrue(result); OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test"]); } From a05c994396f1827c38e1c9cd7b4f5721fe7d4745 Mon Sep 17 00:00:00 2001 From: Alban DEFLANDRE Date: Thu, 22 Apr 2021 18:21:18 +0200 Subject: [PATCH 4/4] Format universal link test case --- .../darwin/ios/framework/Source/FlutterAppDelegateTest.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm index ff1fba8202d99..0282c4f6da6b5 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm @@ -60,7 +60,7 @@ - (void)skip_testLaunchUniversalLink { BOOL result = [appDelegate application:[UIApplication sharedApplication] continueUserActivity:userActivity - restorationHandler: ^(NSArray>* __nullable restorableObjects){ + restorationHandler:^(NSArray>* __nullable restorableObjects){ }]; XCTAssertTrue(result); OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test"]);