mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Added the ability to set the initial route via launch urls. (flutter/engine#21336)
This commit is contained in:
parent
b89bd6a46f
commit
4fb47626da
@ -936,6 +936,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Info.plist
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelayTest.mm
|
||||
|
||||
@ -206,6 +206,7 @@ shared_library("ios_test_flutter") {
|
||||
"//build/config:symbol_visibility_hidden",
|
||||
]
|
||||
sources = [
|
||||
"framework/Source/FlutterAppDelegateTest.mm",
|
||||
"framework/Source/FlutterBinaryMessengerRelayTest.mm",
|
||||
"framework/Source/FlutterDartProjectTest.mm",
|
||||
"framework/Source/FlutterEngineTest.mm",
|
||||
|
||||
@ -4,15 +4,21 @@
|
||||
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h"
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#import "flutter/fml/logging.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h"
|
||||
|
||||
static NSString* kUIBackgroundMode = @"UIBackgroundModes";
|
||||
static NSString* kRemoteNotificationCapabitiliy = @"remote-notification";
|
||||
static NSString* kBackgroundFetchCapatibility = @"fetch";
|
||||
|
||||
@interface FlutterAppDelegate ()
|
||||
@property(nonatomic, copy) FlutterViewController* (^rootFlutterViewControllerGetter)(void);
|
||||
@end
|
||||
|
||||
@implementation FlutterAppDelegate {
|
||||
FlutterPluginAppLifeCycleDelegate* _lifeCycleDelegate;
|
||||
}
|
||||
@ -26,6 +32,7 @@ static NSString* kBackgroundFetchCapatibility = @"fetch";
|
||||
|
||||
- (void)dealloc {
|
||||
[_lifeCycleDelegate release];
|
||||
[_rootFlutterViewControllerGetter release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@ -41,10 +48,13 @@ static NSString* kBackgroundFetchCapatibility = @"fetch";
|
||||
|
||||
// Returns the key window's rootViewController, if it's a FlutterViewController.
|
||||
// Otherwise, returns nil.
|
||||
+ (FlutterViewController*)rootFlutterViewController {
|
||||
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
if ([viewController isKindOfClass:[FlutterViewController class]]) {
|
||||
return (FlutterViewController*)viewController;
|
||||
- (FlutterViewController*)rootFlutterViewController {
|
||||
if (_rootFlutterViewControllerGetter != nil) {
|
||||
return _rootFlutterViewControllerGetter();
|
||||
}
|
||||
UIViewController* rootViewController = _window.rootViewController;
|
||||
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
|
||||
return (FlutterViewController*)rootViewController;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@ -124,7 +134,28 @@ static NSString* kBackgroundFetchCapatibility = @"fetch";
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
openURL:(NSURL*)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
|
||||
return [_lifeCycleDelegate application:application openURL:url options:options];
|
||||
if ([_lifeCycleDelegate application:application openURL:url options:options]) {
|
||||
return YES;
|
||||
} else {
|
||||
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 an URL.";
|
||||
} else {
|
||||
[flutterViewController.engine.navigationChannel invokeMethod:@"pushRoute"
|
||||
arguments:url.path];
|
||||
}
|
||||
}];
|
||||
return YES;
|
||||
} else {
|
||||
FML_LOG(ERROR) << "Attempting to open an URL without a Flutter RootViewController.";
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
|
||||
@ -175,27 +206,25 @@ static NSString* kBackgroundFetchCapatibility = @"fetch";
|
||||
#pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController
|
||||
|
||||
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
|
||||
UIViewController* rootViewController = _window.rootViewController;
|
||||
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
|
||||
return
|
||||
[[(FlutterViewController*)rootViewController pluginRegistry] registrarForPlugin:pluginKey];
|
||||
FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
|
||||
if (flutterRootViewController) {
|
||||
return [[flutterRootViewController pluginRegistry] registrarForPlugin:pluginKey];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)hasPlugin:(NSString*)pluginKey {
|
||||
UIViewController* rootViewController = _window.rootViewController;
|
||||
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
|
||||
return [[(FlutterViewController*)rootViewController pluginRegistry] hasPlugin:pluginKey];
|
||||
FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
|
||||
if (flutterRootViewController) {
|
||||
return [[flutterRootViewController pluginRegistry] hasPlugin:pluginKey];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
|
||||
UIViewController* rootViewController = _window.rootViewController;
|
||||
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
|
||||
return [[(FlutterViewController*)rootViewController pluginRegistry]
|
||||
valuePublishedByPlugin:pluginKey];
|
||||
FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
|
||||
if (flutterRootViewController) {
|
||||
return [[flutterRootViewController pluginRegistry] valuePublishedByPlugin:pluginKey];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import <OCMock/OCMock.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h"
|
||||
|
||||
FLUTTER_ASSERT_ARC
|
||||
|
||||
@interface FlutterAppDelegateTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation FlutterAppDelegateTest
|
||||
|
||||
- (void)testLaunchUrl {
|
||||
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://example.com"];
|
||||
NSDictionary<UIApplicationOpenURLOptionsKey, id>* options = @{};
|
||||
BOOL result = [appDelegate application:[UIApplication sharedApplication]
|
||||
openURL:url
|
||||
options:options];
|
||||
XCTAssertTrue(result);
|
||||
OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:url.path]);
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,9 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
@class FlutterViewController;
|
||||
|
||||
@interface FlutterAppDelegate (Test)
|
||||
@property(nonatomic, copy) FlutterViewController* (^rootFlutterViewControllerGetter)(void);
|
||||
@end
|
||||
@ -883,6 +883,19 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
[self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
|
||||
}
|
||||
|
||||
- (void)waitForFirstFrame:(NSTimeInterval)timeout
|
||||
callback:(void (^_Nonnull)(BOOL didTimeout))callback {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
|
||||
dispatch_async(queue, ^{
|
||||
fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
|
||||
BOOL didTimeout =
|
||||
self.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(didTimeout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation FlutterEngineRegistrar {
|
||||
|
||||
@ -100,4 +100,17 @@ FLUTTER_ASSERT_ARC
|
||||
message:encodedSetInitialRouteMethod]);
|
||||
}
|
||||
|
||||
- (void)testWaitForFirstFrameTimeout {
|
||||
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
|
||||
[engine run];
|
||||
XCTestExpectation* timeoutFirstFrame = [self expectationWithDescription:@"timeoutFirstFrame"];
|
||||
[engine waitForFirstFrame:0.1
|
||||
callback:^(BOOL didTimeout) {
|
||||
if (timeoutFirstFrame) {
|
||||
[timeoutFirstFrame fulfill];
|
||||
}
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
- (void)notifyLowMemory;
|
||||
- (flutter::PlatformViewIOS*)iosPlatformView;
|
||||
|
||||
- (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback;
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERENGINE_INTERNAL_H_
|
||||
|
||||
@ -4,7 +4,10 @@
|
||||
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h"
|
||||
|
||||
@class FlutterBinaryMessengerRelay;
|
||||
|
||||
// Category to add test-only visibility.
|
||||
@interface FlutterEngine (Test) <FlutterBinaryMessenger>
|
||||
- (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger;
|
||||
- (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback;
|
||||
@end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user