Delegate AppDelegate life-cycle callbacks to plugins via separate object (flutter/engine#5173)

* Move the handling of delegating AppDelegate callback out of FlutterAppDelegate.

Also moves the plugin registry to FlutterViewController. So each view-controller will handle its
own plugins.

This is intended to simplify including one or more Flutter views in an existing iOS app and giving
more precise control of plugin registration.

Fixes: https://github.com/flutter/flutter/issues/16539

* formatting

* Update license golden file
This commit is contained in:
Sigurd Meldgaard 2018-05-17 13:13:22 +02:00 committed by GitHub
parent 9e5c5ac0b9
commit f8d59eeabc
9 changed files with 567 additions and 222 deletions

View File

@ -22,6 +22,7 @@ _flutter_framework_headers = [
"framework/Headers/FlutterMacros.h",
"framework/Headers/FlutterNavigationController.h",
"framework/Headers/FlutterPlugin.h",
"framework/Headers/FlutterPluginAppLifeCycleDelegate.h",
"framework/Headers/FlutterTexture.h",
"framework/Headers/FlutterViewController.h",
]
@ -43,6 +44,7 @@ shared_library("create_flutter_framework_dylib") {
"framework/Source/FlutterNavigationController.mm",
"framework/Source/FlutterPlatformPlugin.h",
"framework/Source/FlutterPlatformPlugin.mm",
"framework/Source/FlutterPluginAppLifeCycleDelegate.mm",
"framework/Source/FlutterStandardCodec.mm",
"framework/Source/FlutterStandardCodec_Internal.h",
"framework/Source/FlutterTextInputDelegate.h",

View File

@ -43,6 +43,7 @@
#include "FlutterMacros.h"
#include "FlutterNavigationController.h"
#include "FlutterPlugin.h"
#include "FlutterPluginAppLifeCycleDelegate.h"
#include "FlutterTexture.h"
#include "FlutterViewController.h"

View File

@ -24,22 +24,10 @@
* code as necessary from FlutterAppDelegate.mm.
*/
FLUTTER_EXPORT
@interface FlutterAppDelegate : UIResponder<UIApplicationDelegate, FlutterPluginRegistry>
@interface FlutterAppDelegate : UIResponder<UIApplicationDelegate, FlutterPluginRegistry, FlutterAppLifeCycleProvider>
@property(strong, nonatomic) UIWindow* window;
// Can be overriden by subclasses to provide a custom FlutterBinaryMessenger,
// typically a FlutterViewController, for plugin interop.
//
// Defaults to window's rootViewController.
- (NSObject<FlutterBinaryMessenger>*)binaryMessenger;
// Can be overriden by subclasses to provide a custom FlutterTextureRegistry,
// typically a FlutterViewController, for plugin interop.
//
// Defaults to window's rootViewController.
- (NSObject<FlutterTextureRegistry>*)textures;
@end
#endif // FLUTTER_FLUTTERDARTPROJECT_H_

View File

@ -253,6 +253,14 @@ NS_ASSUME_NONNULL_BEGIN
- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey;
@end
/**
Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register themselves to the application
life cycle events.
*/
@protocol FlutterAppLifeCycleProvider
- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate;
@end
NS_ASSUME_NONNULL_END;
#endif // FLUTTER_FLUTTERPLUGIN_H_

View File

@ -0,0 +1,135 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERPLUGINAPPLIFECYCLEDELEGATE_H_
#define FLUTTER_FLUTTERPLUGINAPPLIFECYCLEDELEGATE_H_
#include "FlutterPlugin.h"
NS_ASSUME_NONNULL_BEGIN
/**
Propagates `UIAppDelegate` callbacks to registered plugins.
*/
FLUTTER_EXPORT
@interface FlutterPluginAppLifeCycleDelegate : NSObject
/**
Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate as long as it is alive.
`delegate` will only referenced weakly.
*/
- (void)addDelegate:(NSObject<FlutterPlugin>*)delegate;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
- Returns: `NO` if any plugin vetoes application launch.
*/
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationDidBecomeActive:(UIApplication*)application;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationWillResignActive:(UIApplication*)application;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationDidEnterBackground:(UIApplication*)application;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationWillEnterForeground:(UIApplication*)application;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationWillTerminate:(UIApplication*)application;
/**
Called if this plugin has been registered for `UIApplicationDelegate` callbacks.
*/
- (void)application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)application:(UIApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)application:(UIApplication*)application
didReceiveRemoteNotification:(NSDictionary*)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
- Returns: `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
- Returns: `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
- Returns: `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
sourceApplication:(NSString*)sourceApplication
annotation:(id)annotation;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)application:(UIApplication*)application
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler
API_AVAILABLE(ios(9.0));
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
- Returns: `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
completionHandler:(nonnull void (^)())completionHandler;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
- Returns: `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
@end
NS_ASSUME_NONNULL_END
#endif // FLUTTER_FLUTTERPLUGINAPPLIFECYCLEDELEGATE_H_

View File

@ -11,10 +11,11 @@
#include "FlutterBinaryMessenger.h"
#include "FlutterDartProject.h"
#include "FlutterMacros.h"
#include "FlutterPlugin.h"
#include "FlutterTexture.h"
FLUTTER_EXPORT
@interface FlutterViewController : UIViewController<FlutterBinaryMessenger, FlutterTextureRegistry>
@interface FlutterViewController : UIViewController<FlutterBinaryMessenger, FlutterTextureRegistry, FlutterPluginRegistry>
- (instancetype)initWithProject:(FlutterDartProject*)project
nibName:(NSString*)nibNameOrNil
@ -49,6 +50,8 @@ FLUTTER_EXPORT
*/
- (void)setInitialRoute:(NSString*)route;
- (id<FlutterPluginRegistry>)pluginRegistry;
@end
#endif // FLUTTER_FLUTTERVIEWCONTROLLER_H_

View File

@ -3,46 +3,28 @@
// found in the LICENSE file.
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h"
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h"
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#include "lib/fxl/logging.h"
@interface FlutterAppDelegate ()
@property(readonly, nonatomic) NSMutableArray* pluginDelegates;
@property(readonly, nonatomic) NSMutableDictionary* pluginPublications;
@end
@interface FlutterAppDelegateRegistrar : NSObject <FlutterPluginRegistrar>
- (instancetype)initWithPlugin:(NSString*)pluginKey appDelegate:(FlutterAppDelegate*)delegate;
@end
@implementation FlutterAppDelegate {
UIBackgroundTaskIdentifier _debugBackgroundTask;
FlutterPluginAppLifeCycleDelegate* _lifeCycleDelegate;
}
- (instancetype)init {
if (self = [super init]) {
_pluginDelegates = [NSMutableArray new];
_pluginPublications = [NSMutableDictionary new];
_lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
}
return self;
}
- (void)dealloc {
[_pluginDelegates release];
[_pluginPublications release];
[_lifeCycleDelegate release];
[super dealloc];
}
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
if (![plugin application:application didFinishLaunchingWithOptions:launchOptions]) {
return NO;
}
}
}
return YES;
return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
}
// Returns the key window's rootViewController, if it's a FlutterViewController.
@ -65,257 +47,118 @@
}
- (void)applicationDidEnterBackground:(UIApplication*)application {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// The following keeps the Flutter session alive when the device screen locks
// in debug mode. It allows continued use of features like hot reload and
// taking screenshots once the device unlocks again.
//
// Note the name is not an identifier and multiple instances can exist.
_debugBackgroundTask = [application
beginBackgroundTaskWithName:@"Flutter debug task"
expirationHandler:^{
FXL_LOG(WARNING)
<< "\nThe OS has terminated the Flutter debug connection for being "
"inactive in the background for too long.\n\n"
"There are no errors with your Flutter application.\n\n"
"To reconnect, launch your application again via 'flutter run'";
}];
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationDidEnterBackground:application];
}
}
[_lifeCycleDelegate applicationDidEnterBackground:application];
}
- (void)applicationWillEnterForeground:(UIApplication*)application {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
[application endBackgroundTask:_debugBackgroundTask];
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationWillEnterForeground:application];
}
}
[_lifeCycleDelegate applicationWillEnterForeground:application];
}
- (void)applicationWillResignActive:(UIApplication*)application {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationWillResignActive:application];
}
}
[_lifeCycleDelegate applicationWillResignActive:application];
}
- (void)applicationDidBecomeActive:(UIApplication*)application {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationDidBecomeActive:application];
}
}
[_lifeCycleDelegate applicationDidBecomeActive:application];
}
- (void)applicationWillTerminate:(UIApplication*)application {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationWillTerminate:application];
}
}
[_lifeCycleDelegate applicationWillTerminate:application];
}
- (void)application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
[plugin application:application didRegisterUserNotificationSettings:notificationSettings];
}
}
[_lifeCycleDelegate application:application
didRegisterUserNotificationSettings:notificationSettings];
}
- (void)application:(UIApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
[plugin application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
}
[_lifeCycleDelegate application:application
didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication*)application
didReceiveRemoteNotification:(NSDictionary*)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application
didReceiveRemoteNotification:userInfo
fetchCompletionHandler:completionHandler]) {
return;
}
}
}
[_lifeCycleDelegate application:application
didReceiveRemoteNotification:userInfo
fetchCompletionHandler:completionHandler];
}
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application openURL:url options:options]) {
return YES;
}
}
}
return NO;
return [_lifeCycleDelegate application:application openURL:url options:options];
}
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application handleOpenURL:url]) {
return YES;
}
}
}
return NO;
return [_lifeCycleDelegate application:application handleOpenURL:url];
}
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
sourceApplication:(NSString*)sourceApplication
annotation:(id)annotation {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation]) {
return YES;
}
}
}
return NO;
return [_lifeCycleDelegate application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
- (void)application:(UIApplication*)application
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler NS_AVAILABLE_IOS(9_0) {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application
performActionForShortcutItem:shortcutItem
completionHandler:completionHandler]) {
return;
}
}
}
[_lifeCycleDelegate application:application
performActionForShortcutItem:shortcutItem
completionHandler:completionHandler];
}
- (void)application:(UIApplication*)application
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
completionHandler:(nonnull void (^)())completionHandler {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application
handleEventsForBackgroundURLSession:identifier
completionHandler:completionHandler]) {
return;
}
}
}
[_lifeCycleDelegate application:application
handleEventsForBackgroundURLSession:identifier
completionHandler:completionHandler];
}
- (void)application:(UIApplication*)application
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application performFetchWithCompletionHandler:completionHandler]) {
return;
}
}
}
[_lifeCycleDelegate application:application performFetchWithCompletionHandler:completionHandler];
}
// TODO(xster): move when doing https://github.com/flutter/flutter/issues/3671.
- (NSObject<FlutterBinaryMessenger>*)binaryMessenger {
UIViewController* rootViewController = _window.rootViewController;
if ([rootViewController conformsToProtocol:@protocol(FlutterBinaryMessenger)]) {
return (NSObject<FlutterBinaryMessenger>*)rootViewController;
}
return nil;
}
- (NSObject<FlutterTextureRegistry>*)textures {
UIViewController* rootViewController = _window.rootViewController;
if ([rootViewController conformsToProtocol:@protocol(FlutterTextureRegistry)]) {
return (NSObject<FlutterTextureRegistry>*)rootViewController;
}
return nil;
}
#pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
self.pluginPublications[pluginKey] = [NSNull null];
return
[[[FlutterAppDelegateRegistrar alloc] initWithPlugin:pluginKey appDelegate:self] autorelease];
UIViewController* rootViewController = _window.rootViewController;
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
return
[[(FlutterViewController*)rootViewController pluginRegistry] registrarForPlugin:pluginKey];
}
return nil;
}
- (BOOL)hasPlugin:(NSString*)pluginKey {
return _pluginPublications[pluginKey] != nil;
UIViewController* rootViewController = _window.rootViewController;
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
return [[(FlutterViewController*)rootViewController pluginRegistry] hasPlugin:pluginKey];
}
return nil;
}
- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
return _pluginPublications[pluginKey];
}
@end
@implementation FlutterAppDelegateRegistrar {
NSString* _pluginKey;
FlutterAppDelegate* _appDelegate;
UIViewController* rootViewController = _window.rootViewController;
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
return [[(FlutterViewController*)rootViewController pluginRegistry]
valuePublishedByPlugin:pluginKey];
}
return nil;
}
- (instancetype)initWithPlugin:(NSString*)pluginKey appDelegate:(FlutterAppDelegate*)appDelegate {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_pluginKey = [pluginKey retain];
_appDelegate = [appDelegate retain];
return self;
}
#pragma mark - FlutterAppLifeCycleProvider methods
- (void)dealloc {
[_pluginKey release];
[_appDelegate release];
[super dealloc];
}
- (NSObject<FlutterBinaryMessenger>*)messenger {
return [_appDelegate binaryMessenger];
}
- (NSObject<FlutterTextureRegistry>*)textures {
return [_appDelegate textures];
}
- (void)publish:(NSObject*)value {
_appDelegate.pluginPublications[_pluginKey] = value;
}
- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
channel:(FlutterMethodChannel*)channel {
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[delegate handleMethodCall:call result:result];
}];
}
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
[_appDelegate.pluginDelegates addObject:delegate];
}
- (NSString*)lookupKeyForAsset:(NSString*)asset {
return [FlutterDartProject lookupKeyForAsset:asset];
}
- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate {
[_lifeCycleDelegate addDelegate:delegate];
}
@end

View File

@ -0,0 +1,278 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h"
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#include "lib/fxl/logging.h"
@implementation FlutterPluginAppLifeCycleDelegate {
UIBackgroundTaskIdentifier _debugBackgroundTask;
// Weak references to registered plugins.
NSPointerArray* _pluginDelegates;
}
- (instancetype)init {
if (self = [super init]) {
_pluginDelegates = [[NSPointerArray weakObjectsPointerArray] retain];
}
return self;
}
- (void)dealloc {
[_pluginDelegates release];
[super dealloc];
}
static BOOL isPowerOfTwo(NSUInteger x) {
return x != 0 && (x & (x - 1)) == 0;
}
- (void)addDelegate:(NSObject<FlutterPlugin>*)delegate {
[_pluginDelegates addPointer:(__bridge void*)delegate];
if (isPowerOfTwo([_pluginDelegates count])) {
[_pluginDelegates compact];
}
}
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
for (id<FlutterPlugin> plugin in [_pluginDelegates allObjects]) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
if (![plugin application:application didFinishLaunchingWithOptions:launchOptions]) {
return NO;
}
}
}
return YES;
}
// 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;
}
return nil;
}
- (void)applicationDidEnterBackground:(UIApplication*)application {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// The following keeps the Flutter session alive when the device screen locks
// in debug mode. It allows continued use of features like hot reload and
// taking screenshots once the device unlocks again.
//
// Note the name is not an identifier and multiple instances can exist.
_debugBackgroundTask = [application
beginBackgroundTaskWithName:@"Flutter debug task"
expirationHandler:^{
FXL_LOG(WARNING)
<< "\nThe OS has terminated the Flutter debug connection for being "
"inactive in the background for too long.\n\n"
"There are no errors with your Flutter application.\n\n"
"To reconnect, launch your application again via 'flutter run'";
}];
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationDidEnterBackground:application];
}
}
}
- (void)applicationWillEnterForeground:(UIApplication*)application {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
[application endBackgroundTask:_debugBackgroundTask];
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationWillEnterForeground:application];
}
}
}
- (void)applicationWillResignActive:(UIApplication*)application {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationWillResignActive:application];
}
}
}
- (void)applicationDidBecomeActive:(UIApplication*)application {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationDidBecomeActive:application];
}
}
}
- (void)applicationWillTerminate:(UIApplication*)application {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
[plugin applicationWillTerminate:application];
}
}
}
- (void)application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
[plugin application:application didRegisterUserNotificationSettings:notificationSettings];
}
}
}
- (void)application:(UIApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
[plugin application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
}
}
- (void)application:(UIApplication*)application
didReceiveRemoteNotification:(NSDictionary*)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application
didReceiveRemoteNotification:userInfo
fetchCompletionHandler:completionHandler]) {
return;
}
}
}
}
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application openURL:url options:options]) {
return YES;
}
}
}
return NO;
}
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application handleOpenURL:url]) {
return YES;
}
}
}
return NO;
}
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
sourceApplication:(NSString*)sourceApplication
annotation:(id)annotation {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation]) {
return YES;
}
}
}
return NO;
}
- (void)application:(UIApplication*)application
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler NS_AVAILABLE_IOS(9_0) {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application
performActionForShortcutItem:shortcutItem
completionHandler:completionHandler]) {
return;
}
}
}
}
- (BOOL)application:(UIApplication*)application
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
completionHandler:(nonnull void (^)())completionHandler {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application
handleEventsForBackgroundURLSession:identifier
completionHandler:completionHandler]) {
return YES;
}
}
}
return NO;
}
- (BOOL)application:(UIApplication*)application
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
for (id<FlutterPlugin> plugin in _pluginDelegates) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
if ([plugin application:application performFetchWithCompletionHandler:completionHandler]) {
return YES;
}
}
}
return NO;
}
@end

View File

@ -22,6 +22,12 @@
#include "flutter/shell/platform/darwin/ios/platform_view_ios.h"
@interface FlutterViewController () <FlutterTextInputDelegate>
@property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
@end
@interface FlutterViewControllerRegistrar : NSObject <FlutterPluginRegistrar>
- (instancetype)initWithPlugin:(NSString*)pluginKey
flutterViewController:(FlutterViewController*)flutterViewController;
@end
@implementation FlutterViewController {
@ -93,6 +99,8 @@
if ([self setupShell]) {
[self setupChannels];
[self setupNotificationCenterObservers];
_pluginPublications = [NSMutableDictionary new];
}
}
@ -426,6 +434,7 @@
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_pluginPublications release];
[super dealloc];
}
@ -971,4 +980,82 @@ constexpr CGFloat kStandardStatusBarHeight = 20.0;
return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
}
- (id<FlutterPluginRegistry>)pluginRegistry {
return self;
}
#pragma mark - FlutterPluginRegistry
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
self.pluginPublications[pluginKey] = [NSNull null];
return
[[FlutterViewControllerRegistrar alloc] initWithPlugin:pluginKey flutterViewController:self];
}
- (BOOL)hasPlugin:(NSString*)pluginKey {
return _pluginPublications[pluginKey] != nil;
}
- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
return _pluginPublications[pluginKey];
}
@end
@implementation FlutterViewControllerRegistrar {
NSString* _pluginKey;
FlutterViewController* _flutterViewController;
}
- (instancetype)initWithPlugin:(NSString*)pluginKey
flutterViewController:(FlutterViewController*)flutterViewController {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_pluginKey = [pluginKey retain];
_flutterViewController = [flutterViewController retain];
return self;
}
- (void)dealloc {
[_pluginKey release];
[_flutterViewController release];
[super dealloc];
}
- (NSObject<FlutterBinaryMessenger>*)messenger {
return _flutterViewController;
}
- (NSObject<FlutterTextureRegistry>*)textures {
return _flutterViewController;
}
- (void)publish:(NSObject*)value {
_flutterViewController.pluginPublications[_pluginKey] = value;
}
- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
channel:(FlutterMethodChannel*)channel {
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[delegate handleMethodCall:call result:result];
}];
}
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
id<FlutterAppLifeCycleProvider> lifeCycleProvider =
(id<FlutterAppLifeCycleProvider>)appDelegate;
[lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
}
}
- (NSString*)lookupKeyForAsset:(NSString*)asset {
return [_flutterViewController lookupKeyForAsset:asset];
}
- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
return [_flutterViewController lookupKeyForAsset:asset fromPackage:package];
}
@end