7 #include "flutter/fml/paths.h"
8 #include "flutter/lib/ui/plugins/callback_cache.h"
9 #import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
19 @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:),
20 @selector(application:performFetchWithCompletionHandler:)};
23 - (void)handleDidEnterBackground:(NSNotification*)notification
24 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
25 - (void)handleWillEnterForeground:(NSNotification*)notification
26 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
27 - (void)handleWillResignActive:(NSNotification*)notification
28 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
29 - (void)handleDidBecomeActive:(NSNotification*)notification
30 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
31 - (void)handleWillTerminate:(NSNotification*)notification
32 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
34 @property(nonatomic, assign) BOOL didForwardApplicationWillLaunch;
35 @property(nonatomic, assign) BOOL didForwardApplicationDidLaunch;
39 UIBackgroundTaskIdentifier _debugBackgroundTask;
45 - (void)addObserverFor:(NSString*)name selector:(
SEL)selector {
46 [[NSNotificationCenter defaultCenter] addObserver:self selector:selector name:name object:nil];
49 - (instancetype)init {
50 if (
self = [super init]) {
54 [
self addObserverFor:UIApplicationDidEnterBackgroundNotification
55 selector:@selector(handleDidEnterBackground:)];
56 [
self addObserverFor:UIApplicationWillEnterForegroundNotification
57 selector:@selector(handleWillEnterForeground:)];
58 [
self addObserverFor:UIApplicationWillResignActiveNotification
59 selector:@selector(handleWillResignActive:)];
60 [
self addObserverFor:UIApplicationDidBecomeActiveNotification
61 selector:@selector(handleDidBecomeActive:)];
62 [
self addObserverFor:UIApplicationWillTerminateNotification
63 selector:@selector(handleWillTerminate:)];
65 _delegates = [NSPointerArray weakObjectsPointerArray];
66 _debugBackgroundTask = UIBackgroundTaskInvalid;
71 static BOOL IsPowerOfTwo(NSUInteger x) {
72 return x != 0 && (x & (x - 1)) == 0;
75 - (BOOL)isSelectorAddedDynamically:(
SEL)selector {
77 if (selector == aSelector) {
84 - (BOOL)hasPluginThatRespondsToSelector:(
SEL)selector {
85 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
89 if ([delegate respondsToSelector:selector]) {
96 - (BOOL)appSupportsSceneLifecycle {
111 return [delegate conformsToProtocol:@protocol(FlutterSceneLifeCycleDelegate)];
115 [_delegates addPointer:(__bridge void*)delegate];
117 [_delegates compact];
121 - (void)sceneFallbackDidFinishLaunchingApplication:(UIApplication*)application {
124 if (
self.didForwardApplicationDidLaunch) {
128 [
self application:application didFinishLaunchingWithOptions:@{}];
131 - (void)sceneFallbackWillFinishLaunchingApplication:(UIApplication*)application {
134 if (
self.didForwardApplicationWillLaunch) {
139 if (
self.didForwardApplicationDidLaunch) {
143 [
self application:application willFinishLaunchingWithOptions:@{}];
146 - (BOOL)application:(UIApplication*)application
147 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
149 self.didForwardApplicationDidLaunch = YES;
151 return [
self application:application
152 didFinishLaunchingWithOptions:launchOptions
153 isFallbackForScene:NO];
156 - (BOOL)sceneWillConnectFallback:(UISceneConnectionOptions*)connectionOptions {
161 NSDictionary<UIApplicationLaunchOptionsKey, id>* convertedLaunchOptions =
162 ConvertConnectionOptions(connectionOptions);
163 if (convertedLaunchOptions.count == 0) {
167 if (![
self application:application
168 didFinishLaunchingWithOptions:convertedLaunchOptions
169 isFallbackForScene:YES]) {
175 - (BOOL)application:(UIApplication*)application
176 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
177 isFallbackForScene:(BOOL)isFallback {
180 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
181 if (!delegate || (isFallback && [
self pluginSupportsSceneLifecycle:delegate])) {
184 if ([delegate respondsToSelector:
@selector(application:didFinishLaunchingWithOptions:)]) {
185 if (![delegate application:application didFinishLaunchingWithOptions:launchOptions]) {
203 static NSDictionary<UIApplicationLaunchOptionsKey, id>* ConvertConnectionOptions(
204 UISceneConnectionOptions* connectionOptions) {
205 NSMutableDictionary<UIApplicationLaunchOptionsKey, id>* convertedOptions =
206 [NSMutableDictionary dictionary];
208 if (connectionOptions.shortcutItem) {
209 convertedOptions[UIApplicationLaunchOptionsShortcutItemKey] = connectionOptions.shortcutItem;
211 if (connectionOptions.sourceApplication) {
212 convertedOptions[UIApplicationLaunchOptionsSourceApplicationKey] =
213 connectionOptions.sourceApplication;
215 if (connectionOptions.URLContexts.anyObject.URL) {
216 convertedOptions[UIApplicationLaunchOptionsURLKey] =
217 connectionOptions.URLContexts.anyObject.URL;
219 return convertedOptions;
222 - (BOOL)application:(UIApplication*)application
223 willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
225 self.didForwardApplicationWillLaunch = YES;
227 flutter::DartCallbackCache::LoadCacheFromDisk();
228 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
232 if ([delegate respondsToSelector:_cmd]) {
233 if (![delegate application:application willFinishLaunchingWithOptions:launchOptions]) {
241 - (void)handleDidEnterBackground:(NSNotification*)notification
242 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
243 if ([
self appSupportsSceneLifecycle]) {
246 UIApplication* application = [UIApplication sharedApplication];
247 #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
253 _debugBackgroundTask = [application
254 beginBackgroundTaskWithName:@"Flutter debug task"
256 if (_debugBackgroundTask != UIBackgroundTaskInvalid) {
257 [application endBackgroundTask:_debugBackgroundTask];
258 _debugBackgroundTask = UIBackgroundTaskInvalid;
261 logWarning:@"\nThe OS has terminated the Flutter debug connection for being "
262 "inactive in the background for too long.\n\n"
263 "There are no errors with your Flutter application.\n\n"
264 "To reconnect, launch your application again via 'flutter run'"];
267 [
self applicationDidEnterBackground:application isFallbackForScene:NO];
270 - (void)sceneDidEnterBackgroundFallback {
275 [
self applicationDidEnterBackground:application isFallbackForScene:YES];
278 - (void)applicationDidEnterBackground:(UIApplication*)application
279 isFallbackForScene:(BOOL)isFallback {
280 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
281 if (!delegate || (isFallback && [
self pluginSupportsSceneLifecycle:delegate])) {
284 if ([delegate respondsToSelector:
@selector(applicationDidEnterBackground:)]) {
285 [delegate applicationDidEnterBackground:application];
290 - (void)handleWillEnterForeground:(NSNotification*)notification
291 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
292 if ([
self appSupportsSceneLifecycle]) {
295 UIApplication* application = [UIApplication sharedApplication];
296 #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
297 if (_debugBackgroundTask != UIBackgroundTaskInvalid) {
298 [application endBackgroundTask:_debugBackgroundTask];
299 _debugBackgroundTask = UIBackgroundTaskInvalid;
302 [
self applicationWillEnterForeground:application isFallbackForScene:NO];
305 - (void)sceneWillEnterForegroundFallback {
310 [
self applicationWillEnterForeground:application isFallbackForScene:YES];
313 - (void)applicationWillEnterForeground:(UIApplication*)application
314 isFallbackForScene:(BOOL)isFallback {
315 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
316 if (!delegate || (isFallback && [
self pluginSupportsSceneLifecycle:delegate])) {
319 if ([delegate respondsToSelector:
@selector(applicationWillEnterForeground:)]) {
320 [delegate applicationWillEnterForeground:application];
325 - (void)handleWillResignActive:(NSNotification*)notification
326 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
327 if ([
self appSupportsSceneLifecycle]) {
330 UIApplication* application = [UIApplication sharedApplication];
331 [
self applicationWillResignActive:application isFallbackForScene:NO];
334 - (void)sceneWillResignActiveFallback {
339 [
self applicationWillResignActive:application isFallbackForScene:YES];
342 - (void)applicationWillResignActive:(UIApplication*)application
343 isFallbackForScene:(BOOL)isFallback {
344 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
345 if (!delegate || (isFallback && [
self pluginSupportsSceneLifecycle:delegate])) {
348 if ([delegate respondsToSelector:
@selector(applicationWillResignActive:)]) {
349 [delegate applicationWillResignActive:application];
354 - (void)handleDidBecomeActive:(NSNotification*)notification
355 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
356 if ([
self appSupportsSceneLifecycle]) {
359 UIApplication* application = [UIApplication sharedApplication];
360 [
self applicationDidBecomeActive:application isFallbackForScene:NO];
363 - (void)sceneDidBecomeActiveFallback {
368 [
self applicationDidBecomeActive:application isFallbackForScene:YES];
371 - (void)applicationDidBecomeActive:(UIApplication*)application isFallbackForScene:(BOOL)isFallback {
372 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
373 if (!delegate || (isFallback && [
self pluginSupportsSceneLifecycle:delegate])) {
376 if ([delegate respondsToSelector:
@selector(applicationDidBecomeActive:)]) {
377 [delegate applicationDidBecomeActive:application];
382 - (void)handleWillTerminate:(NSNotification*)notification
383 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
384 UIApplication* application = [UIApplication sharedApplication];
385 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
389 if ([delegate respondsToSelector:
@selector(applicationWillTerminate:)]) {
390 [delegate applicationWillTerminate:application];
395 #pragma GCC diagnostic push
396 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
397 - (void)application:(UIApplication*)application
398 didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
399 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
403 if ([delegate respondsToSelector:_cmd]) {
404 [delegate application:application didRegisterUserNotificationSettings:notificationSettings];
408 #pragma GCC diagnostic pop
410 - (void)application:(UIApplication*)application
411 didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
412 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
416 if ([delegate respondsToSelector:_cmd]) {
417 [delegate application:application
418 didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
423 - (void)application:(UIApplication*)application
424 didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
425 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
429 if ([delegate respondsToSelector:_cmd]) {
430 [delegate application:application didFailToRegisterForRemoteNotificationsWithError:error];
435 - (void)application:(UIApplication*)application
436 didReceiveRemoteNotification:(NSDictionary*)userInfo
437 fetchCompletionHandler:(
void (^)(UIBackgroundFetchResult result))completionHandler {
438 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
442 if ([delegate respondsToSelector:_cmd]) {
443 if ([delegate application:application
444 didReceiveRemoteNotification:userInfo
445 fetchCompletionHandler:completionHandler]) {
452 #pragma GCC diagnostic push
453 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
454 - (void)application:(UIApplication*)application
455 didReceiveLocalNotification:(UILocalNotification*)notification {
456 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
460 if ([delegate respondsToSelector:_cmd]) {
461 [delegate application:application didReceiveLocalNotification:notification];
465 #pragma GCC diagnostic pop
467 - (void)userNotificationCenter:(UNUserNotificationCenter*)center
468 willPresentNotification:(UNNotification*)notification
469 withCompletionHandler:
470 (
void (^)(UNNotificationPresentationOptions options))completionHandler {
471 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
472 if ([delegate respondsToSelector:_cmd]) {
473 [delegate userNotificationCenter:center
474 willPresentNotification:notification
475 withCompletionHandler:completionHandler];
480 - (void)userNotificationCenter:(UNUserNotificationCenter*)center
481 didReceiveNotificationResponse:(UNNotificationResponse*)response
482 withCompletionHandler:(
void (^)(
void))completionHandler {
483 for (id<FlutterApplicationLifeCycleDelegate> delegate in
_delegates.allObjects) {
484 if ([delegate respondsToSelector:_cmd]) {
485 [delegate userNotificationCenter:center
486 didReceiveNotificationResponse:response
487 withCompletionHandler:completionHandler];
492 - (BOOL)application:(UIApplication*)application
494 options:(NSDictionary<UIApplicationOpenURLOptionsKey,
id>*)options {
495 return [
self application:application openURL:url options:options isFallbackForScene:NO];
498 - (BOOL)sceneFallbackOpenURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts {
499 for (UIOpenURLContext* context in URLContexts) {
502 options:ConvertOptions(context.options)
503 isFallbackForScene:YES]) {
510 - (BOOL)application:(UIApplication*)application
512 options:(NSDictionary<UIApplicationOpenURLOptionsKey,
id>*)options
513 isFallbackForScene:(BOOL)isFallback {
514 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
515 if (!delegate || (isFallback && [
self pluginSupportsSceneLifecycle:delegate])) {
518 if ([delegate respondsToSelector:
@selector(application:openURL:options:)]) {
519 if ([delegate application:application openURL:url options:options]) {
536 static NSDictionary<UIApplicationOpenURLOptionsKey, id>* ConvertOptions(
537 UISceneOpenURLOptions* options) {
538 NSMutableDictionary<UIApplicationOpenURLOptionsKey, id>* convertedOptions =
539 [NSMutableDictionary dictionary];
540 if (options.sourceApplication) {
541 convertedOptions[UIApplicationOpenURLOptionsSourceApplicationKey] = options.sourceApplication;
543 if (options.annotation) {
544 convertedOptions[UIApplicationOpenURLOptionsAnnotationKey] = options.annotation;
546 convertedOptions[UIApplicationOpenURLOptionsOpenInPlaceKey] = @(options.openInPlace);
547 if (@available(iOS 14.5, *)) {
548 if (options.eventAttribution) {
549 convertedOptions[UIApplicationOpenURLOptionsEventAttributionKey] = options.eventAttribution;
552 return convertedOptions;
555 - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
556 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
560 if ([delegate respondsToSelector:_cmd]) {
561 if ([delegate application:application handleOpenURL:url]) {
569 - (BOOL)application:(UIApplication*)application
571 sourceApplication:(NSString*)sourceApplication
572 annotation:(
id)annotation {
573 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
577 if ([delegate respondsToSelector:_cmd]) {
578 if ([delegate application:application
580 sourceApplication:sourceApplication
581 annotation:annotation]) {
589 - (void)application:(UIApplication*)application
590 performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
591 completionHandler:(
void (^)(BOOL succeeded))completionHandler {
592 [
self application:application
593 performActionForShortcutItem:shortcutItem
594 completionHandler:completionHandler
595 isFallbackForScene:NO];
598 - (BOOL)sceneFallbackPerformActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
599 completionHandler:(
void (^)(BOOL succeeded))completionHandler {
604 return [
self application:application
605 performActionForShortcutItem:shortcutItem
606 completionHandler:completionHandler
607 isFallbackForScene:YES];
610 - (BOOL)application:(UIApplication*)application
611 performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
612 completionHandler:(
void (^)(BOOL succeeded))completionHandler
613 isFallbackForScene:(BOOL)isFallback {
614 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
615 if (!delegate || (isFallback && [
self pluginSupportsSceneLifecycle:delegate])) {
618 if ([delegate respondsToSelector:
@selector(application:
619 performActionForShortcutItem:completionHandler:)]) {
620 if ([delegate application:application
621 performActionForShortcutItem:shortcutItem
622 completionHandler:completionHandler]) {
630 - (BOOL)application:(UIApplication*)application
631 handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
632 completionHandler:(nonnull
void (^)())completionHandler {
633 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
637 if ([delegate respondsToSelector:_cmd]) {
638 if ([delegate application:application
639 handleEventsForBackgroundURLSession:identifier
640 completionHandler:completionHandler]) {
648 - (BOOL)application:(UIApplication*)application
649 performFetchWithCompletionHandler:(
void (^)(UIBackgroundFetchResult result))completionHandler {
650 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
654 if ([delegate respondsToSelector:_cmd]) {
655 if ([delegate application:application performFetchWithCompletionHandler:completionHandler]) {
663 - (BOOL)application:(UIApplication*)application
664 continueUserActivity:(NSUserActivity*)userActivity
665 restorationHandler:(
void (^)(NSArray*))restorationHandler {
666 return [
self application:application
667 continueUserActivity:userActivity
668 restorationHandler:restorationHandler
669 isFallbackForScene:NO];
672 - (BOOL)sceneFallbackContinueUserActivity:(NSUserActivity*)userActivity {
677 return [
self application:application
678 continueUserActivity:userActivity
679 restorationHandler:nil
680 isFallbackForScene:YES];
683 - (BOOL)application:(UIApplication*)application
684 continueUserActivity:(NSUserActivity*)userActivity
685 restorationHandler:(
void (^)(NSArray*))restorationHandler
686 isFallbackForScene:(BOOL)isFallback {
687 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in
_delegates.allObjects) {
688 if (!delegate || (isFallback && [
self pluginSupportsSceneLifecycle:delegate])) {
691 if ([delegate respondsToSelector:
@selector(application:
692 continueUserActivity:restorationHandler:)]) {
693 if ([delegate application:application
694 continueUserActivity:userActivity
695 restorationHandler:restorationHandler]) {
static const SEL kSelectorsHandledByPlugins[]
NSPointerArray * _delegates
static FLUTTER_ASSERT_ARC const char * kCallbackCacheSubDir
void setCachePath:(NSString *path)
UIApplication * application