Flutter Engine
 
Loading...
Searching...
No Matches
FlutterPluginAppLifeCycleDelegate.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include "flutter/fml/paths.h"
9#import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
13
15
16static const char* kCallbackCacheSubDir = "Library/Caches/";
17
18static const SEL kSelectorsHandledByPlugins[] = {
19 @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:),
20 @selector(application:performFetchWithCompletionHandler:)};
21
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");
33
34@property(nonatomic, assign) BOOL didForwardApplicationWillLaunch;
35@property(nonatomic, assign) BOOL didForwardApplicationDidLaunch;
36@end
37
39 UIBackgroundTaskIdentifier _debugBackgroundTask;
40
41 // Weak references to registered plugins.
42 NSPointerArray* _delegates;
43}
44
45- (void)addObserverFor:(NSString*)name selector:(SEL)selector {
46 [[NSNotificationCenter defaultCenter] addObserver:self selector:selector name:name object:nil];
47}
48
49- (instancetype)init {
50 if (self = [super init]) {
51 std::string cachePath = fml::paths::JoinPaths({getenv("HOME"), kCallbackCacheSubDir});
52 [FlutterCallbackCache setCachePath:[NSString stringWithUTF8String:cachePath.c_str()]];
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:)];
64 }
65 _delegates = [NSPointerArray weakObjectsPointerArray];
66 _debugBackgroundTask = UIBackgroundTaskInvalid;
67 }
68 return self;
69}
70
71static BOOL IsPowerOfTwo(NSUInteger x) {
72 return x != 0 && (x & (x - 1)) == 0;
73}
74
75- (BOOL)isSelectorAddedDynamically:(SEL)selector {
76 for (const SEL& aSelector : kSelectorsHandledByPlugins) {
77 if (selector == aSelector) {
78 return YES;
79 }
80 }
81 return NO;
82}
83
84- (BOOL)hasPluginThatRespondsToSelector:(SEL)selector {
85 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in [_delegates allObjects]) {
86 if (!delegate) {
87 continue;
88 }
89 if ([delegate respondsToSelector:selector]) {
90 return YES;
91 }
92 }
93 return NO;
94}
95
96- (BOOL)appSupportsSceneLifecycle {
97 // When UIScene lifecycle is being used, some application lifecycle events are not call by UIKit.
98 // However, the notifications are still sent. When a Flutter app has been migrated to UIScene,
99 // Flutter should not use the notifications to forward application events to plugins since they
100 // are not expected to be called.
101 // See https://flutter.dev/go/ios-ui-scene-lifecycle-migration?tab=t.0#heading=h.eq8gyd4ds50u
103}
104
105- (BOOL)pluginSupportsSceneLifecycle:(NSObject<FlutterApplicationLifeCycleDelegate>*)delegate {
106 // The fallback is unnecessary if the plugin conforms to FlutterSceneLifeCycleDelegate.
107 // This means that the plugin has migrated to scene lifecycle events and shouldn't require
108 // application events. However, the plugin may still have the application event implemented to
109 // maintain compatibility with un-migrated apps, which is why the fallback should be checked
110 // before checking that the delegate responds to the selector.
111 return [delegate conformsToProtocol:@protocol(FlutterSceneLifeCycleDelegate)];
112}
113
114- (void)addDelegate:(NSObject<FlutterApplicationLifeCycleDelegate>*)delegate {
115 [_delegates addPointer:(__bridge void*)delegate];
116 if (IsPowerOfTwo([_delegates count])) {
117 [_delegates compact];
118 }
119}
120
121- (void)sceneFallbackDidFinishLaunchingApplication:(UIApplication*)application {
122 // If the application:didFinishingLaunchingWithOptions: event has already been sent to plugins, do
123 // not send again.
124 if (self.didForwardApplicationDidLaunch) {
125 return;
126 }
127 // Send nil launchOptions since UIKit sends nil when UIScene is enabled.
128 [self application:application didFinishLaunchingWithOptions:@{}];
129}
130
131- (void)sceneFallbackWillFinishLaunchingApplication:(UIApplication*)application {
132 // If the application:willFinishLaunchingWithOptions: event has already been sent to plugins, do
133 // not send again.
134 if (self.didForwardApplicationWillLaunch) {
135 return;
136 }
137 // If the application:didFinishingLaunchingWithOptions: event has already been sent to plugins, do
138 // not send willFinishLaunchingWithOptions since it should happen before, not after.
139 if (self.didForwardApplicationDidLaunch) {
140 return;
141 }
142 // Send nil launchOptions since UIKit sends nil when UIScene is enabled.
143 [self application:application willFinishLaunchingWithOptions:@{}];
144}
145
146- (BOOL)application:(UIApplication*)application
147 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
148 if (_delegates.count > 0) {
149 self.didForwardApplicationDidLaunch = YES;
150 }
151 return [self application:application
152 didFinishLaunchingWithOptions:launchOptions
153 isFallbackForScene:NO];
154}
155
156- (BOOL)sceneWillConnectFallback:(UISceneConnectionOptions*)connectionOptions {
157 UIApplication* application = FlutterSharedApplication.application;
158 if (!application) {
159 return NO;
160 }
161 NSDictionary<UIApplicationLaunchOptionsKey, id>* convertedLaunchOptions =
162 ConvertConnectionOptions(connectionOptions);
163 if (convertedLaunchOptions.count == 0) {
164 // Only use fallback if there are meaningful launch options.
165 return NO;
166 }
167 if (![self application:application
168 didFinishLaunchingWithOptions:convertedLaunchOptions
169 isFallbackForScene:YES]) {
170 return YES;
171 }
172 return NO;
173}
174
175- (BOOL)application:(UIApplication*)application
176 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
177 isFallbackForScene:(BOOL)isFallback {
178 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
179 if (!delegate || (isFallback && [self pluginSupportsSceneLifecycle:delegate])) {
180 continue;
181 }
182 if ([delegate respondsToSelector:@selector(application:didFinishLaunchingWithOptions:)]) {
183 if (![delegate application:application didFinishLaunchingWithOptions:launchOptions]) {
184 return NO;
185 }
186 }
187 }
188 return YES;
189}
190
191/* Makes a best attempt to convert UISceneConnectionOptions from the scene event
192 * (`scene:willConnectToSession:options:`) to a NSDictionary of options used to the application
193 * lifecycle event.
194 *
195 * For more information on UISceneConnectionOptions, see
196 * https://developer.apple.com/documentation/uikit/uiscene/connectionoptions.
197 *
198 * For information about the possible keys in the NSDictionary and how to handle them, see
199 * https://developer.apple.com/documentation/uikit/uiapplication/launchoptionskey
200 */
201static NSDictionary<UIApplicationLaunchOptionsKey, id>* ConvertConnectionOptions(
202 UISceneConnectionOptions* connectionOptions) {
203 NSMutableDictionary<UIApplicationLaunchOptionsKey, id>* convertedOptions =
204 [NSMutableDictionary dictionary];
205
206 if (connectionOptions.shortcutItem) {
207 convertedOptions[UIApplicationLaunchOptionsShortcutItemKey] = connectionOptions.shortcutItem;
208 }
209 if (connectionOptions.sourceApplication) {
210 convertedOptions[UIApplicationLaunchOptionsSourceApplicationKey] =
211 connectionOptions.sourceApplication;
212 }
213 if (connectionOptions.URLContexts.anyObject.URL) {
214 convertedOptions[UIApplicationLaunchOptionsURLKey] =
215 connectionOptions.URLContexts.anyObject.URL;
216 }
217 return convertedOptions;
218}
219
220- (BOOL)application:(UIApplication*)application
221 willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
222 if (_delegates.count > 0) {
223 self.didForwardApplicationWillLaunch = YES;
224 }
226 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in [_delegates allObjects]) {
227 if (!delegate) {
228 continue;
229 }
230 if ([delegate respondsToSelector:_cmd]) {
231 if (![delegate application:application willFinishLaunchingWithOptions:launchOptions]) {
232 return NO;
233 }
234 }
235 }
236 return YES;
237}
238
239- (void)handleDidEnterBackground:(NSNotification*)notification
240 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
241 if ([self appSupportsSceneLifecycle]) {
242 return;
243 }
244 UIApplication* application = [UIApplication sharedApplication];
245#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
246 // The following keeps the Flutter session alive when the device screen locks
247 // in debug mode. It allows continued use of features like hot reload and
248 // taking screenshots once the device unlocks again.
249 //
250 // Note the name is not an identifier and multiple instances can exist.
251 _debugBackgroundTask = [application
252 beginBackgroundTaskWithName:@"Flutter debug task"
253 expirationHandler:^{
254 if (_debugBackgroundTask != UIBackgroundTaskInvalid) {
255 [application endBackgroundTask:_debugBackgroundTask];
256 _debugBackgroundTask = UIBackgroundTaskInvalid;
257 }
258 [FlutterLogger
259 logWarning:@"\nThe OS has terminated the Flutter debug connection for being "
260 "inactive in the background for too long.\n\n"
261 "There are no errors with your Flutter application.\n\n"
262 "To reconnect, launch your application again via 'flutter run'"];
263 }];
264#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
265 [self applicationDidEnterBackground:application isFallbackForScene:NO];
266}
267
268- (void)sceneDidEnterBackgroundFallback {
269 UIApplication* application = FlutterSharedApplication.application;
270 if (!application) {
271 return;
272 }
273 [self applicationDidEnterBackground:application isFallbackForScene:YES];
274}
275
276- (void)applicationDidEnterBackground:(UIApplication*)application
277 isFallbackForScene:(BOOL)isFallback {
278 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
279 if (!delegate || (isFallback && [self pluginSupportsSceneLifecycle:delegate])) {
280 continue;
281 }
282 if ([delegate respondsToSelector:@selector(applicationDidEnterBackground:)]) {
283 [delegate applicationDidEnterBackground:application];
284 }
285 }
286}
287
288- (void)handleWillEnterForeground:(NSNotification*)notification
289 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
290 if ([self appSupportsSceneLifecycle]) {
291 return;
292 }
293 UIApplication* application = [UIApplication sharedApplication];
294#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
295 if (_debugBackgroundTask != UIBackgroundTaskInvalid) {
296 [application endBackgroundTask:_debugBackgroundTask];
297 _debugBackgroundTask = UIBackgroundTaskInvalid;
298 }
299#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
300 [self applicationWillEnterForeground:application isFallbackForScene:NO];
301}
302
303- (void)sceneWillEnterForegroundFallback {
304 UIApplication* application = FlutterSharedApplication.application;
305 if (!application) {
306 return;
307 }
308 [self applicationWillEnterForeground:application isFallbackForScene:YES];
309}
310
311- (void)applicationWillEnterForeground:(UIApplication*)application
312 isFallbackForScene:(BOOL)isFallback {
313 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
314 if (!delegate || (isFallback && [self pluginSupportsSceneLifecycle:delegate])) {
315 continue;
316 }
317 if ([delegate respondsToSelector:@selector(applicationWillEnterForeground:)]) {
318 [delegate applicationWillEnterForeground:application];
319 }
320 }
321}
322
323- (void)handleWillResignActive:(NSNotification*)notification
324 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
325 if ([self appSupportsSceneLifecycle]) {
326 return;
327 }
328 UIApplication* application = [UIApplication sharedApplication];
329 [self applicationWillResignActive:application isFallbackForScene:NO];
330}
331
332- (void)sceneWillResignActiveFallback {
333 UIApplication* application = FlutterSharedApplication.application;
334 if (!application) {
335 return;
336 }
337 [self applicationWillResignActive:application isFallbackForScene:YES];
338}
339
340- (void)applicationWillResignActive:(UIApplication*)application
341 isFallbackForScene:(BOOL)isFallback {
342 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
343 if (!delegate || (isFallback && [self pluginSupportsSceneLifecycle:delegate])) {
344 continue;
345 }
346 if ([delegate respondsToSelector:@selector(applicationWillResignActive:)]) {
347 [delegate applicationWillResignActive:application];
348 }
349 }
350}
351
352- (void)handleDidBecomeActive:(NSNotification*)notification
353 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
354 if ([self appSupportsSceneLifecycle]) {
355 return;
356 }
357 UIApplication* application = [UIApplication sharedApplication];
358 [self applicationDidBecomeActive:application isFallbackForScene:NO];
359}
360
361- (void)sceneDidBecomeActiveFallback {
362 UIApplication* application = FlutterSharedApplication.application;
363 if (!application) {
364 return;
365 }
366 [self applicationDidBecomeActive:application isFallbackForScene:YES];
367}
368
369- (void)applicationDidBecomeActive:(UIApplication*)application isFallbackForScene:(BOOL)isFallback {
370 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
371 if (!delegate || (isFallback && [self pluginSupportsSceneLifecycle:delegate])) {
372 continue;
373 }
374 if ([delegate respondsToSelector:@selector(applicationDidBecomeActive:)]) {
375 [delegate applicationDidBecomeActive:application];
376 }
377 }
378}
379
380- (void)handleWillTerminate:(NSNotification*)notification
381 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
382 UIApplication* application = [UIApplication sharedApplication];
383 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
384 if (!delegate) {
385 continue;
386 }
387 if ([delegate respondsToSelector:@selector(applicationWillTerminate:)]) {
388 [delegate applicationWillTerminate:application];
389 }
390 }
391}
392
393#pragma GCC diagnostic push
394#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
395- (void)application:(UIApplication*)application
396 didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
397 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
398 if (!delegate) {
399 continue;
400 }
401 if ([delegate respondsToSelector:_cmd]) {
402 [delegate application:application didRegisterUserNotificationSettings:notificationSettings];
403 }
404 }
405}
406#pragma GCC diagnostic pop
407
408- (void)application:(UIApplication*)application
409 didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
410 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
411 if (!delegate) {
412 continue;
413 }
414 if ([delegate respondsToSelector:_cmd]) {
415 [delegate application:application
416 didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
417 }
418 }
419}
420
421- (void)application:(UIApplication*)application
422 didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
423 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
424 if (!delegate) {
425 continue;
426 }
427 if ([delegate respondsToSelector:_cmd]) {
428 [delegate application:application didFailToRegisterForRemoteNotificationsWithError:error];
429 }
430 }
431}
432
433- (void)application:(UIApplication*)application
434 didReceiveRemoteNotification:(NSDictionary*)userInfo
435 fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
436 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
437 if (!delegate) {
438 continue;
439 }
440 if ([delegate respondsToSelector:_cmd]) {
441 if ([delegate application:application
442 didReceiveRemoteNotification:userInfo
443 fetchCompletionHandler:completionHandler]) {
444 return;
445 }
446 }
447 }
448}
449
450#pragma GCC diagnostic push
451#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
452- (void)application:(UIApplication*)application
453 didReceiveLocalNotification:(UILocalNotification*)notification {
454 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
455 if (!delegate) {
456 continue;
457 }
458 if ([delegate respondsToSelector:_cmd]) {
459 [delegate application:application didReceiveLocalNotification:notification];
460 }
461 }
462}
463#pragma GCC diagnostic pop
464
465- (void)userNotificationCenter:(UNUserNotificationCenter*)center
466 willPresentNotification:(UNNotification*)notification
467 withCompletionHandler:
468 (void (^)(UNNotificationPresentationOptions options))completionHandler {
469 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
470 if ([delegate respondsToSelector:_cmd]) {
471 [delegate userNotificationCenter:center
472 willPresentNotification:notification
473 withCompletionHandler:completionHandler];
474 }
475 }
476}
477
478- (void)userNotificationCenter:(UNUserNotificationCenter*)center
479 didReceiveNotificationResponse:(UNNotificationResponse*)response
480 withCompletionHandler:(void (^)(void))completionHandler {
481 for (id<FlutterApplicationLifeCycleDelegate> delegate in _delegates) {
482 if ([delegate respondsToSelector:_cmd]) {
483 [delegate userNotificationCenter:center
484 didReceiveNotificationResponse:response
485 withCompletionHandler:completionHandler];
486 }
487 }
488}
489
490- (BOOL)application:(UIApplication*)application
491 openURL:(NSURL*)url
492 options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
493 return [self application:application openURL:url options:options isFallbackForScene:NO];
494}
495
496- (BOOL)sceneFallbackOpenURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts {
497 for (UIOpenURLContext* context in URLContexts) {
498 if ([self application:FlutterSharedApplication.application
499 openURL:context.URL
500 options:ConvertOptions(context.options)
501 isFallbackForScene:YES]) {
502 return YES;
503 };
504 };
505 return NO;
506}
507
508- (BOOL)application:(UIApplication*)application
509 openURL:(NSURL*)url
510 options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options
511 isFallbackForScene:(BOOL)isFallback {
512 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
513 if (!delegate || (isFallback && [self pluginSupportsSceneLifecycle:delegate])) {
514 continue;
515 }
516 if ([delegate respondsToSelector:@selector(application:openURL:options:)]) {
517 if ([delegate application:application openURL:url options:options]) {
518 return YES;
519 }
520 }
521 }
522 return NO;
523}
524
525/* Converts UISceneOpenURLOptions from the scene event (`sceneFallbackOpenURLContexts`) to a
526 * NSDictionary of options used to the application lifecycle event.
527 *
528 * For more information on UISceneOpenURLOptions, see
529 * https://developer.apple.com/documentation/uikit/uiopenurlcontext/options.
530 *
531 * For information about the possible keys in the NSDictionary and how to handle them, see
532 * https://developer.apple.com/documentation/uikit/uiapplication/openurloptionskey
533 */
534static NSDictionary<UIApplicationOpenURLOptionsKey, id>* ConvertOptions(
535 UISceneOpenURLOptions* options) {
536 NSMutableDictionary<UIApplicationOpenURLOptionsKey, id>* convertedOptions =
537 [NSMutableDictionary dictionary];
538 if (options.sourceApplication) {
539 convertedOptions[UIApplicationOpenURLOptionsSourceApplicationKey] = options.sourceApplication;
540 }
541 if (options.annotation) {
542 convertedOptions[UIApplicationOpenURLOptionsAnnotationKey] = options.annotation;
543 }
544 convertedOptions[UIApplicationOpenURLOptionsOpenInPlaceKey] = @(options.openInPlace);
545 if (@available(iOS 14.5, *)) {
546 if (options.eventAttribution) {
547 convertedOptions[UIApplicationOpenURLOptionsEventAttributionKey] = options.eventAttribution;
548 }
549 }
550 return convertedOptions;
551}
552
553- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
554 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
555 if (!delegate) {
556 continue;
557 }
558 if ([delegate respondsToSelector:_cmd]) {
559 if ([delegate application:application handleOpenURL:url]) {
560 return YES;
561 }
562 }
563 }
564 return NO;
565}
566
567- (BOOL)application:(UIApplication*)application
568 openURL:(NSURL*)url
569 sourceApplication:(NSString*)sourceApplication
570 annotation:(id)annotation {
571 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
572 if (!delegate) {
573 continue;
574 }
575 if ([delegate respondsToSelector:_cmd]) {
576 if ([delegate application:application
577 openURL:url
578 sourceApplication:sourceApplication
579 annotation:annotation]) {
580 return YES;
581 }
582 }
583 }
584 return NO;
585}
586
587- (void)application:(UIApplication*)application
588 performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
589 completionHandler:(void (^)(BOOL succeeded))completionHandler {
590 [self application:application
591 performActionForShortcutItem:shortcutItem
592 completionHandler:completionHandler
593 isFallbackForScene:NO];
594}
595
596- (BOOL)sceneFallbackPerformActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
597 completionHandler:(void (^)(BOOL succeeded))completionHandler {
598 UIApplication* application = FlutterSharedApplication.application;
599 if (!application) {
600 return NO;
601 }
602 return [self application:application
603 performActionForShortcutItem:shortcutItem
604 completionHandler:completionHandler
605 isFallbackForScene:YES];
606}
607
608- (BOOL)application:(UIApplication*)application
609 performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
610 completionHandler:(void (^)(BOOL succeeded))completionHandler
611 isFallbackForScene:(BOOL)isFallback {
612 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
613 if (!delegate || (isFallback && [self pluginSupportsSceneLifecycle:delegate])) {
614 continue;
615 }
616 if ([delegate respondsToSelector:@selector(application:
617 performActionForShortcutItem:completionHandler:)]) {
618 if ([delegate application:application
619 performActionForShortcutItem:shortcutItem
620 completionHandler:completionHandler]) {
621 return YES;
622 }
623 }
624 }
625 return NO;
626}
627
628- (BOOL)application:(UIApplication*)application
629 handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
630 completionHandler:(nonnull void (^)())completionHandler {
631 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
632 if (!delegate) {
633 continue;
634 }
635 if ([delegate respondsToSelector:_cmd]) {
636 if ([delegate application:application
637 handleEventsForBackgroundURLSession:identifier
638 completionHandler:completionHandler]) {
639 return YES;
640 }
641 }
642 }
643 return NO;
644}
645
646- (BOOL)application:(UIApplication*)application
647 performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
648 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
649 if (!delegate) {
650 continue;
651 }
652 if ([delegate respondsToSelector:_cmd]) {
653 if ([delegate application:application performFetchWithCompletionHandler:completionHandler]) {
654 return YES;
655 }
656 }
657 }
658 return NO;
659}
660
661- (BOOL)application:(UIApplication*)application
662 continueUserActivity:(NSUserActivity*)userActivity
663 restorationHandler:(void (^)(NSArray*))restorationHandler {
664 return [self application:application
665 continueUserActivity:userActivity
666 restorationHandler:restorationHandler
667 isFallbackForScene:NO];
668}
669
670- (BOOL)sceneFallbackContinueUserActivity:(NSUserActivity*)userActivity {
671 UIApplication* application = FlutterSharedApplication.application;
672 if (!application) {
673 return NO;
674 }
675 return [self application:application
676 continueUserActivity:userActivity
677 restorationHandler:nil
678 isFallbackForScene:YES];
679}
680
681- (BOOL)application:(UIApplication*)application
682 continueUserActivity:(NSUserActivity*)userActivity
683 restorationHandler:(void (^)(NSArray*))restorationHandler
684 isFallbackForScene:(BOOL)isFallback {
685 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
686 if (!delegate || (isFallback && [self pluginSupportsSceneLifecycle:delegate])) {
687 continue;
688 }
689 if ([delegate respondsToSelector:@selector(application:
690 continueUserActivity:restorationHandler:)]) {
691 if ([delegate application:application
692 continueUserActivity:userActivity
693 restorationHandler:restorationHandler]) {
694 return YES;
695 }
696 }
697 }
698 return NO;
699}
700@end
static const SEL kSelectorsHandledByPlugins[]
NSPointerArray * _delegates
static FLUTTER_ASSERT_ARC const char * kCallbackCacheSubDir
int32_t x
void setCachePath:(NSString *path)
std::string JoinPaths(std::initializer_list< std::string > components)
Definition paths.cc:14
int BOOL