diff --git a/shell/platform/darwin/ios/framework/Headers/Flutter.h b/shell/platform/darwin/ios/framework/Headers/Flutter.h index 364912afc04a8..9135c8200603c 100644 --- a/shell/platform/darwin/ios/framework/Headers/Flutter.h +++ b/shell/platform/darwin/ios/framework/Headers/Flutter.h @@ -8,6 +8,9 @@ /** BREAKING CHANGES: + December 17, 2018: + - Changed designated initializer on FlutterEngine + October 5, 2018: - Removed FlutterNavigationController.h/.mm - Changed return signature of `FlutterDartHeadlessCodeRunner.run*` from void diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h index 1d435acd80b7f..21a36cdd6fff2 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h @@ -55,8 +55,28 @@ FLUTTER_EXPORT * the threads used by this FlutterEngine. * @param projectOrNil The `FlutterDartProject` to run. */ +- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil; + +/** + * Initialize this FlutterEngine with a `FlutterDartProject`. + * + * If the FlutterDartProject is not specified, the FlutterEngine will attempt to locate + * the project in a default location (the flutter_assets folder in the iOS application + * bundle). + * + * A newly initialized engine will not run the `FlutterDartProject` until either + * `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI:` is called. + * + * @param labelPrefix The label prefix used to identify threads for this instance. Should + * be unique across FlutterEngine instances, and is used in instrumentation to label + * the threads used by this FlutterEngine. + * @param projectOrNil The `FlutterDartProject` to run. + * @param allowHeadlessExecution Whether or not to allow this instance to continue + * running after passing a nil `FlutterViewController` to `-setViewController:`. + */ - (instancetype)initWithName:(NSString*)labelPrefix - project:(FlutterDartProject*)projectOrNil NS_DESIGNATED_INITIALIZER; + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER; /** * The default initializer is not available for this object. diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h index e51dc6d5068c0..24acbc08ad6fe 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h @@ -46,8 +46,25 @@ FLUTTER_DEPRECATED("FlutterEngine should be used rather than FlutterHeadlessDart * be unique across FlutterEngine instances * @param projectOrNil The `FlutterDartProject` to run. */ +- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil; + +/** + * Iniitalize this FlutterHeadlessDartRunner with a `FlutterDartProject`. + * + * If the FlutterDartProject is not specified, the FlutterHeadlessDartRunner will attempt to locate + * the project in a default location. + * + * A newly initialized engine will not run the `FlutterDartProject` until either + * `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI` is called. + * + * @param labelPrefix The label prefix used to identify threads for this instance. Should + * be unique across FlutterEngine instances + * @param projectOrNil The `FlutterDartProject` to run. + * @param allowHeadlessExecution Must be set to `YES`. + */ - (instancetype)initWithName:(NSString*)labelPrefix - project:(FlutterDartProject*)projectOrNil NS_DESIGNATED_INITIALIZER; + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER; /** * Not recommended for use - will initialize with a default label ("io.flutter.headless") diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h index 53f3adc855d5d..371a888c88394 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h @@ -151,6 +151,11 @@ FLUTTER_EXPORT */ @property(nonatomic, getter=isViewOpaque) BOOL viewOpaque; +/** + * The `FlutterEngine` instance for this view controller. + */ +@property(weak, nonatomic, readonly) FlutterEngine* engine; + @end #endif // FLUTTER_FLUTTERVIEWCONTROLLER_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index cace5a8ba8aa2..9daf06683083d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -60,12 +60,22 @@ @implementation FlutterEngine { fml::scoped_nsobject _settingsChannel; int64_t _nextTextureId; + + BOOL _allowHeadlessExecution; } - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil { + return [self initWithName:labelPrefix project:projectOrNil allowHeadlessExecution:YES]; +} + +- (instancetype)initWithName:(NSString*)labelPrefix + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution { self = [super init]; NSAssert(self, @"Super init cannot be nil"); NSAssert(labelPrefix, @"labelPrefix is required"); + + _allowHeadlessExecution = allowHeadlessExecution; _labelPrefix = [labelPrefix copy]; _weakFactory = std::make_unique>(self); @@ -76,7 +86,6 @@ - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject* _dartProject.reset([projectOrNil retain]); _pluginPublications = [NSMutableDictionary new]; - _publisher.reset([[FlutterObservatoryPublisher alloc] init]); _platformViewsController.reset(new shell::FlutterPlatformViewsController()); [self setupChannels]; @@ -135,7 +144,14 @@ - (void)setViewController:(FlutterViewController*)viewController { FML_DCHECK(self.iosPlatformView); _viewController = [viewController getWeakPtr]; self.iosPlatformView->SetOwnerViewController(_viewController); - [self maybeSetupPlatformViewChannels]; + if (!viewController && !_allowHeadlessExecution) { + [self resetChannels]; + + _shell.reset(); + _threadHost.Reset(); + } else { + [self maybeSetupPlatformViewChannels]; + } } - (FlutterViewController*)viewController { @@ -176,6 +192,20 @@ - (FlutterBasicMessageChannel*)settingsChannel { return _settingsChannel.get(); } +- (void)resetChannels { + _localizationChannel.reset(); + _navigationChannel.reset(); + _platformChannel.reset(); + _platformViewsChannel.reset(); + _textInputChannel.reset(); + _lifecycleChannel.reset(); + _systemChannel.reset(); + _settingsChannel.reset(); +} + +// If you add a channel, be sure to also update `resetChannels`. +// Channels get a reference to the engine, and therefore need manual +// cleanup for proper collection. - (void)setupChannels { _localizationChannel.reset([[FlutterMethodChannel alloc] initWithName:@"flutter/localization" @@ -221,8 +251,6 @@ - (void)setupChannels { _textInputPlugin.get().textInputDelegate = self; _platformPlugin.reset([[FlutterPlatformPlugin alloc] initWithEngine:[self getWeakPtr]]); - - [self maybeSetupPlatformViewChannels]; } - (void)maybeSetupPlatformViewChannels { @@ -348,6 +376,11 @@ - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: " << entrypoint.UTF8String; } else { + [self setupChannels]; + if (!_platformViewsController) { + _platformViewsController.reset(new shell::FlutterPlatformViewsController()); + } + _publisher.reset([[FlutterObservatoryPublisher alloc] init]); [self maybeSetupPlatformViewChannels]; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm index 8e7cb26d48a1d..6299e9e684474 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm @@ -28,7 +28,17 @@ @implementation FlutterHeadlessDartRunner { } - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil { - return [super initWithName:labelPrefix project:projectOrNil]; + return [self initWithName:labelPrefix project:projectOrNil allowHeadlessExecution:YES]; +} + +- (instancetype)initWithName:(NSString*)labelPrefix + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution { + NSAssert(allowHeadlessExecution == YES, + @"Cannot initialize a FlutterHeadlessDartRunner without headless execution."); + return [super initWithName:labelPrefix + project:projectOrNil + allowHeadlessExecution:allowHeadlessExecution]; } - (instancetype)init { return [self initWithName:@"io.flutter.headless" project:nil]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 4d066b21f9c70..ca73791013b6c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -67,7 +67,9 @@ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil if (self) { _viewOpaque = YES; _weakFactory = std::make_unique>(self); - _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter" project:projectOrNil]); + _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter" + project:projectOrNil + allowHeadlessExecution:NO]); _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); [_engine.get() createShell:nil libraryURI:nil]; _engineNeedsLaunch = YES; @@ -116,8 +118,8 @@ - (void)performCommonViewControllerInitialization { [self setupNotificationCenterObservers]; } -- (fml::scoped_nsobject)engine { - return _engine; +- (FlutterEngine*)engine { + return _engine.get(); } - (fml::WeakPtr)getWeakPtr { @@ -389,9 +391,9 @@ - (void)viewWillAppear:(BOOL)animated { if (_engineNeedsLaunch) { [_engine.get() launchEngine:nil libraryURI:nil]; - [_engine.get() setViewController:self]; _engineNeedsLaunch = NO; } + [_engine.get() setViewController:self]; // Only recreate surface on subsequent appearances when viewport metrics are known. // First time surface creation is done on viewDidLayoutSubviews. @@ -423,7 +425,7 @@ - (void)viewDidDisappear:(BOOL)animated { TRACE_EVENT0("flutter", "viewDidDisappear"); [self surfaceUpdated:NO]; [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"]; - + [_engine.get() setViewController:nil]; [super viewDidDisappear:animated]; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h index 46705b9be8d23..4908c7dc80638 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -17,8 +17,6 @@ - (fml::WeakPtr)getWeakPtr; - (shell::FlutterPlatformViewsController*)platformViewsController; -@property(readonly) fml::scoped_nsobject engine; - @end #endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_