From dae79c8b84a54e6326dd633aafaf84f4bcbac389 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Tue, 3 Aug 2021 15:14:35 -0700 Subject: [PATCH 1/3] Support iOS universal links route deep linking --- .../framework/Source/FlutterAppDelegate.mm | 37 ++++---- .../Source/FlutterAppDelegateTest.mm | 90 ++++++++++++++----- .../Source/FlutterAppDelegate_Test.h | 5 +- .../IosUnitTests.xcodeproj/project.pbxproj | 2 + 4 files changed, 93 insertions(+), 41 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index 32b3fe0eb1e06..45211d3b92cfe 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -141,13 +141,8 @@ static BOOL IsDeepLinkingEnabled(NSDictionary* infoDictionary) { } } -- (BOOL)application:(UIApplication*)application - openURL:(NSURL*)url - options:(NSDictionary*)options - infoPlistGetter:(NSDictionary* (^)())infoPlistGetter { - if ([_lifeCycleDelegate application:application openURL:url options:options]) { - return YES; - } else if (!IsDeepLinkingEnabled(infoPlistGetter())) { +- (BOOL)openURL:(NSURL*)url infoPlistGetter:(NSDictionary* (^)())infoPlistGetter { + if (!IsDeepLinkingEnabled(infoPlistGetter())) { return NO; } else { FlutterViewController* flutterViewController = [self rootFlutterViewController]; @@ -181,12 +176,13 @@ - (BOOL)application:(UIApplication*)application - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url options:(NSDictionary*)options { - return [self application:application - openURL:url - options:options - infoPlistGetter:^NSDictionary*() { - return [[NSBundle mainBundle] infoDictionary]; - }]; + if ([_lifeCycleDelegate application:application openURL:url options:options]) { + return YES; + } + return [self openURL:url + infoPlistGetter:^NSDictionary*() { + return [[NSBundle mainBundle] infoDictionary]; + }]; } - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url { @@ -229,9 +225,18 @@ - (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; + } + if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { + return NO; + } + return [self openURL:userActivity.webpageURL + infoPlistGetter:^NSDictionary*() { + return [[NSBundle mainBundle] infoDictionary]; + }]; } #pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm index 1997d76bc5b2d..1c982e1df0e42 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm @@ -32,13 +32,10 @@ - (void)testLaunchUrl { return viewController; }; NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; - NSDictionary* options = @{}; - BOOL result = [appDelegate application:[UIApplication sharedApplication] - openURL:url - options:options - infoPlistGetter:^NSDictionary*() { - return @{@"FlutterDeepLinkingEnabled" : @(YES)}; - }]; + BOOL result = [appDelegate openURL:url + infoPlistGetter:^NSDictionary*() { + return @{@"FlutterDeepLinkingEnabled" : @YES}; + }]; XCTAssertTrue(result); OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test"]); } @@ -57,13 +54,10 @@ - (void)testLaunchUrlWithQueryParameterAndFragment { return viewController; }; NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test#fragment"]; - NSDictionary* options = @{}; - BOOL result = [appDelegate application:[UIApplication sharedApplication] - openURL:url - options:options - infoPlistGetter:^NSDictionary*() { - return @{@"FlutterDeepLinkingEnabled" : @(YES)}; - }]; + BOOL result = [appDelegate openURL:url + infoPlistGetter:^NSDictionary*() { + return @{@"FlutterDeepLinkingEnabled" : @YES}; + }]; XCTAssertTrue(result); OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test#fragment"]); @@ -83,15 +77,69 @@ - (void)testLaunchUrlWithFragmentNoQueryParameter { return viewController; }; NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route#fragment"]; - NSDictionary* options = @{}; - BOOL result = [appDelegate application:[UIApplication sharedApplication] - openURL:url - options:options - infoPlistGetter:^NSDictionary*() { - return @{@"FlutterDeepLinkingEnabled" : @(YES)}; - }]; + BOOL result = [appDelegate openURL:url + infoPlistGetter:^NSDictionary*() { + return @{@"FlutterDeepLinkingEnabled" : @YES}; + }]; XCTAssertTrue(result); OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route#fragment"]); } +#pragma mark - Deep linking + +- (void)testUniversalLinkWebBrowserUrl { + 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); + // Set blockArg to a strong local to retain to end of scope. + id blockArg = [OCMArg invokeBlockWithArgs:@NO, nil]; + OCMStub([engine waitForFirstFrame:3.0 callback:blockArg]); + appDelegate.rootFlutterViewControllerGetter = ^{ + return viewController; + }; + NSUserActivity* userActivity = + [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb]; + userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; + BOOL result = [appDelegate + application:[UIApplication sharedApplication] + continueUserActivity:userActivity + restorationHandler:^(NSArray>* __nullable restorableObjects){ + }]; + XCTAssertFalse(result); + OCMReject([navigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); +} + +- (void)testUniversalLinkPushRoute { + id mockBundle = OCMClassMock([NSBundle class]); + OCMStub([mockBundle mainBundle]).andReturn(mockBundle); + OCMStub([mockBundle infoDictionary]).andReturn(@{@"FlutterDeepLinkingEnabled" : @YES}); + + 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); + // Set blockArg to a strong local to retain to end of scope. + id blockArg = [OCMArg invokeBlockWithArgs:@NO, nil]; + OCMStub([engine waitForFirstFrame:3.0 callback:blockArg]); + appDelegate.rootFlutterViewControllerGetter = ^{ + return viewController; + }; + NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; + NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:@"com.example.test"]; + 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"]); + [mockBundle stopMocking]; +} + @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h index a4394fde9fdac..cb4151ebbfd79 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h @@ -7,9 +7,6 @@ @interface FlutterAppDelegate (Test) @property(nonatomic, copy) FlutterViewController* (^rootFlutterViewControllerGetter)(void); -- (BOOL)application:(UIApplication*)application - openURL:(NSURL*)url - options:(NSDictionary*)options - infoPlistGetter:(NSDictionary* (^)())infoPlistGetter; +- (BOOL)openURL:(NSURL*)url infoPlistGetter:(NSDictionary* (^)())infoPlistGetter; @end diff --git a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj index 2d30d6669fcf5..e563587fad963 100644 --- a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj +++ b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FlutterEngineConfig.xcconfig; sourceTree = ""; }; F7521D7226BB671E005F15C5 /* libios_test_flutter.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libios_test_flutter.dylib; path = "../../../../out/$(FLUTTER_ENGINE)/libios_test_flutter.dylib"; sourceTree = ""; }; F7521D7526BB673E005F15C5 /* libocmock_shared.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libocmock_shared.dylib; path = "../../../../out/$(FLUTTER_ENGINE)/libocmock_shared.dylib"; sourceTree = ""; }; + F7A3FDE026B9E0A300EADD61 /* FlutterAppDelegateTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterAppDelegateTest.mm; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -93,6 +94,7 @@ 0AC232E924BA71D300A85907 /* Source */ = { isa = PBXGroup; children = ( + F7A3FDE026B9E0A300EADD61 /* FlutterAppDelegateTest.mm */, 0AC232F424BA71D300A85907 /* SemanticsObjectTest.mm */, 0AC232F724BA71D300A85907 /* FlutterEngineTest.mm */, 0AC2330324BA71D300A85907 /* accessibility_bridge_test.mm */, From 9989ab0d57aa1009fed6dd6d4f5e2f986816fe7d Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 4 Aug 2021 18:30:19 -0700 Subject: [PATCH 2/3] Move more logic into test setup, remove infoPlistGetter helper --- .../framework/Source/FlutterAppDelegate.mm | 26 +-- .../Source/FlutterAppDelegateTest.mm | 177 ++++++++++-------- .../Source/FlutterAppDelegate_Test.h | 2 - 3 files changed, 102 insertions(+), 103 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index 45211d3b92cfe..679237a7843ac 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -132,17 +132,11 @@ - (void)userNotificationCenter:(UNUserNotificationCenter*)center } } -static BOOL IsDeepLinkingEnabled(NSDictionary* infoDictionary) { - NSNumber* isEnabled = [infoDictionary objectForKey:@"FlutterDeepLinkingEnabled"]; - if (isEnabled) { - return [isEnabled boolValue]; - } else { - return NO; - } -} - -- (BOOL)openURL:(NSURL*)url infoPlistGetter:(NSDictionary* (^)())infoPlistGetter { - if (!IsDeepLinkingEnabled(infoPlistGetter())) { +- (BOOL)openURL:(NSURL*)url { + NSNumber* isDeepLinkingEnabled = + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]; + if (!isDeepLinkingEnabled.boolValue) { + // Not set or NO. return NO; } else { FlutterViewController* flutterViewController = [self rootFlutterViewController]; @@ -179,10 +173,7 @@ - (BOOL)application:(UIApplication*)application if ([_lifeCycleDelegate application:application openURL:url options:options]) { return YES; } - return [self openURL:url - infoPlistGetter:^NSDictionary*() { - return [[NSBundle mainBundle] infoDictionary]; - }]; + return [self openURL:url]; } - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url { @@ -233,10 +224,7 @@ - (BOOL)application:(UIApplication*)application if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { return NO; } - return [self openURL:userActivity.webpageURL - infoPlistGetter:^NSDictionary*() { - return [[NSBundle mainBundle] infoDictionary]; - }]; + return [self openURL:userActivity.webpageURL]; } #pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm index 1c982e1df0e42..86780bc37ac3c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm @@ -14,132 +14,145 @@ FLUTTER_ASSERT_ARC @interface FlutterAppDelegateTest : XCTestCase +@property(strong) FlutterAppDelegate* appDelegate; + +@property(strong) id mockMainBundle; +@property(strong) id mockNavigationChannel; + +// Retain callback until the tests are done. +// https://github.com/flutter/flutter/issues/74267 +@property(strong) id mockEngineFirstFrameCallback; @end @implementation FlutterAppDelegateTest -- (void)testLaunchUrl { +- (void)setUp { + [super setUp]; + + id mockMainBundle = OCMClassMock([NSBundle class]); + OCMStub([mockMainBundle mainBundle]).andReturn(mockMainBundle); + self.mockMainBundle = mockMainBundle; + FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init]; + self.appDelegate = appDelegate; + FlutterViewController* viewController = OCMClassMock([FlutterViewController class]); - FlutterEngine* engine = OCMClassMock([FlutterEngine class]); FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]); + self.mockNavigationChannel = navigationChannel; + + FlutterEngine* engine = OCMClassMock([FlutterEngine class]); OCMStub([engine navigationChannel]).andReturn(navigationChannel); OCMStub([viewController engine]).andReturn(engine); - // Set blockNoInvoker to a strong local to retain to end of scope. - id blockNoInvoker = [OCMArg invokeBlockWithArgs:@NO, nil]; - OCMStub([engine waitForFirstFrame:3.0 callback:blockNoInvoker]); + + id mockEngineFirstFrameCallback = [OCMArg invokeBlockWithArgs:@NO, nil]; + self.mockEngineFirstFrameCallback = mockEngineFirstFrameCallback; + OCMStub([engine waitForFirstFrame:3.0 callback:mockEngineFirstFrameCallback]); appDelegate.rootFlutterViewControllerGetter = ^{ return viewController; }; - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; - BOOL result = [appDelegate openURL:url - infoPlistGetter:^NSDictionary*() { - return @{@"FlutterDeepLinkingEnabled" : @YES}; - }]; +} + +- (void)tearDown { + // Explicitly stop mocking the NSBundle class property. + [self.mockMainBundle stopMocking]; + [super tearDown]; +} + +- (void)testLaunchUrl { + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) + .andReturn(@YES); + + BOOL result = + [self.appDelegate application:[UIApplication sharedApplication] + openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"] + options:@{}]; XCTAssertTrue(result); - OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test"]); + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" + arguments:@"/custom/route?query=test"]); +} + +- (void)testLaunchUrlWithDeepLinkingNotSet { + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) + .andReturn(nil); + + BOOL result = + [self.appDelegate application:[UIApplication sharedApplication] + openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"] + options:@{}]; + XCTAssertFalse(result); + OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); +} + +- (void)testLaunchUrlWithDeepLinkingDisabled { + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) + .andReturn(@NO); + + BOOL result = + [self.appDelegate application:[UIApplication sharedApplication] + openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"] + options:@{}]; + XCTAssertFalse(result); + OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); } - (void)testLaunchUrlWithQueryParameterAndFragment { - 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); - // Set blockNoInvoker to a strong local to retain to end of scope. - id blockNoInvoker = [OCMArg invokeBlockWithArgs:@NO, nil]; - OCMStub([engine waitForFirstFrame:3.0 callback:blockNoInvoker]); - appDelegate.rootFlutterViewControllerGetter = ^{ - return viewController; - }; - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test#fragment"]; - BOOL result = [appDelegate openURL:url - infoPlistGetter:^NSDictionary*() { - return @{@"FlutterDeepLinkingEnabled" : @YES}; - }]; + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) + .andReturn(@YES); + + BOOL result = [self.appDelegate + application:[UIApplication sharedApplication] + openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test#fragment"] + options:@{}]; XCTAssertTrue(result); - OCMVerify([navigationChannel invokeMethod:@"pushRoute" - arguments:@"/custom/route?query=test#fragment"]); + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" + arguments:@"/custom/route?query=test#fragment"]); } - (void)testLaunchUrlWithFragmentNoQueryParameter { - 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); - // Set blockNoInvoker to a strong local to retain to end of scope. - id blockNoInvoker = [OCMArg invokeBlockWithArgs:@NO, nil]; - OCMStub([engine waitForFirstFrame:3.0 callback:blockNoInvoker]); - appDelegate.rootFlutterViewControllerGetter = ^{ - return viewController; - }; - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route#fragment"]; - BOOL result = [appDelegate openURL:url - infoPlistGetter:^NSDictionary*() { - return @{@"FlutterDeepLinkingEnabled" : @YES}; - }]; + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) + .andReturn(@YES); + + BOOL result = + [self.appDelegate application:[UIApplication sharedApplication] + openURL:[NSURL URLWithString:@"http://myApp/custom/route#fragment"] + options:@{}]; XCTAssertTrue(result); - OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route#fragment"]); + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" + arguments:@"/custom/route#fragment"]); } #pragma mark - Deep linking - (void)testUniversalLinkWebBrowserUrl { - 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); - // Set blockArg to a strong local to retain to end of scope. - id blockArg = [OCMArg invokeBlockWithArgs:@NO, nil]; - OCMStub([engine waitForFirstFrame:3.0 callback:blockArg]); - appDelegate.rootFlutterViewControllerGetter = ^{ - return viewController; - }; + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) + .andReturn(@YES); + NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb]; userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; - BOOL result = [appDelegate + BOOL result = [self.appDelegate application:[UIApplication sharedApplication] continueUserActivity:userActivity restorationHandler:^(NSArray>* __nullable restorableObjects){ }]; XCTAssertFalse(result); - OCMReject([navigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); + OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); } - (void)testUniversalLinkPushRoute { - id mockBundle = OCMClassMock([NSBundle class]); - OCMStub([mockBundle mainBundle]).andReturn(mockBundle); - OCMStub([mockBundle infoDictionary]).andReturn(@{@"FlutterDeepLinkingEnabled" : @YES}); + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) + .andReturn(@YES); - 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); - // Set blockArg to a strong local to retain to end of scope. - id blockArg = [OCMArg invokeBlockWithArgs:@NO, nil]; - OCMStub([engine waitForFirstFrame:3.0 callback:blockArg]); - appDelegate.rootFlutterViewControllerGetter = ^{ - return viewController; - }; - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:@"com.example.test"]; - userActivity.webpageURL = url; - BOOL result = [appDelegate + userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; + BOOL result = [self.appDelegate application:[UIApplication sharedApplication] continueUserActivity:userActivity restorationHandler:^(NSArray>* __nullable restorableObjects){ }]; XCTAssertTrue(result); - OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test"]); - [mockBundle stopMocking]; + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" + arguments:@"/custom/route?query=test"]); } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h index cb4151ebbfd79..d56844a37d5cf 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h @@ -7,6 +7,4 @@ @interface FlutterAppDelegate (Test) @property(nonatomic, copy) FlutterViewController* (^rootFlutterViewControllerGetter)(void); -- (BOOL)openURL:(NSURL*)url infoPlistGetter:(NSDictionary* (^)())infoPlistGetter; - @end From 673d3eb10326a3be1e06a03a346600258e227eca Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Thu, 5 Aug 2021 11:14:31 -0700 Subject: [PATCH 3/3] Remove browser exception --- .../ios/framework/Source/FlutterAppDelegate.mm | 3 --- .../framework/Source/FlutterAppDelegateTest.mm | 16 ---------------- 2 files changed, 19 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index 679237a7843ac..fa68266619ced 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -221,9 +221,6 @@ - (BOOL)application:(UIApplication*)application restorationHandler:restorationHandler]) { return YES; } - if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { - return NO; - } return [self openURL:userActivity.webpageURL]; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm index 86780bc37ac3c..c8c4aa700c91d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm @@ -123,22 +123,6 @@ - (void)testLaunchUrlWithFragmentNoQueryParameter { #pragma mark - Deep linking -- (void)testUniversalLinkWebBrowserUrl { - OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) - .andReturn(@YES); - - NSUserActivity* userActivity = - [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb]; - userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; - BOOL result = [self.appDelegate - application:[UIApplication sharedApplication] - continueUserActivity:userActivity - restorationHandler:^(NSArray>* __nullable restorableObjects){ - }]; - XCTAssertFalse(result); - OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); -} - - (void)testUniversalLinkPushRoute { OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) .andReturn(@YES);