Flutter Engine
The Flutter Engine
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
5#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h"
6
7#include "flutter/fml/logging.h"
8#include "flutter/fml/paths.h"
9#include "flutter/lib/ui/plugins/callback_cache.h"
10#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h"
11
13
14static const char* kCallbackCacheSubDir = "Library/Caches/";
15
16static const SEL kSelectorsHandledByPlugins[] = {
17 @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:),
18 @selector(application:performFetchWithCompletionHandler:)};
19
21- (void)handleDidEnterBackground:(NSNotification*)notification
22 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
23- (void)handleWillEnterForeground:(NSNotification*)notification
24 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
25- (void)handleWillResignActive:(NSNotification*)notification
26 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
27- (void)handleDidBecomeActive:(NSNotification*)notification
28 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
29- (void)handleWillTerminate:(NSNotification*)notification
30 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
31@end
32
34 UIBackgroundTaskIdentifier _debugBackgroundTask;
35
36 // Weak references to registered plugins.
37 NSPointerArray* _delegates;
38}
39
40- (void)addObserverFor:(NSString*)name selector:(SEL)selector {
41 [[NSNotificationCenter defaultCenter] addObserver:self selector:selector name:name object:nil];
42}
43
44- (instancetype)init {
45 if (self = [super init]) {
46 std::string cachePath = fml::paths::JoinPaths({getenv("HOME"), kCallbackCacheSubDir});
47 [FlutterCallbackCache setCachePath:[NSString stringWithUTF8String:cachePath.c_str()]];
48#if not APPLICATION_EXTENSION_API_ONLY
49 [self addObserverFor:UIApplicationDidEnterBackgroundNotification
50 selector:@selector(handleDidEnterBackground:)];
51 [self addObserverFor:UIApplicationWillEnterForegroundNotification
52 selector:@selector(handleWillEnterForeground:)];
53 [self addObserverFor:UIApplicationWillResignActiveNotification
54 selector:@selector(handleWillResignActive:)];
55 [self addObserverFor:UIApplicationDidBecomeActiveNotification
56 selector:@selector(handleDidBecomeActive:)];
57 [self addObserverFor:UIApplicationWillTerminateNotification
58 selector:@selector(handleWillTerminate:)];
59#endif
60 _delegates = [NSPointerArray weakObjectsPointerArray];
61 _debugBackgroundTask = UIBackgroundTaskInvalid;
62 }
63 return self;
64}
65
66static BOOL IsPowerOfTwo(NSUInteger x) {
67 return x != 0 && (x & (x - 1)) == 0;
68}
69
70- (BOOL)isSelectorAddedDynamically:(SEL)selector {
71 for (const SEL& aSelector : kSelectorsHandledByPlugins) {
72 if (selector == aSelector) {
73 return YES;
74 }
75 }
76 return NO;
77}
78
79- (BOOL)hasPluginThatRespondsToSelector:(SEL)selector {
80 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in [_delegates allObjects]) {
81 if (!delegate) {
82 continue;
83 }
84 if ([delegate respondsToSelector:selector]) {
85 return YES;
86 }
87 }
88 return NO;
89}
90
91- (void)addDelegate:(NSObject<FlutterApplicationLifeCycleDelegate>*)delegate {
92 [_delegates addPointer:(__bridge void*)delegate];
93 if (IsPowerOfTwo([_delegates count])) {
94 [_delegates compact];
95 }
96}
97
98- (BOOL)application:(UIApplication*)application
99 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
100 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in [_delegates allObjects]) {
101 if (!delegate) {
102 continue;
103 }
104 if ([delegate respondsToSelector:_cmd]) {
105 if (![delegate application:application didFinishLaunchingWithOptions:launchOptions]) {
106 return NO;
107 }
108 }
109 }
110 return YES;
111}
112
113- (BOOL)application:(UIApplication*)application
114 willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
116 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in [_delegates allObjects]) {
117 if (!delegate) {
118 continue;
119 }
120 if ([delegate respondsToSelector:_cmd]) {
121 if (![delegate application:application willFinishLaunchingWithOptions:launchOptions]) {
122 return NO;
123 }
124 }
125 }
126 return YES;
127}
128
129- (void)handleDidEnterBackground:(NSNotification*)notification
130 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
131 UIApplication* application = [UIApplication sharedApplication];
132#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
133 // The following keeps the Flutter session alive when the device screen locks
134 // in debug mode. It allows continued use of features like hot reload and
135 // taking screenshots once the device unlocks again.
136 //
137 // Note the name is not an identifier and multiple instances can exist.
138 _debugBackgroundTask = [application
139 beginBackgroundTaskWithName:@"Flutter debug task"
140 expirationHandler:^{
141 if (_debugBackgroundTask != UIBackgroundTaskInvalid) {
142 [application endBackgroundTask:_debugBackgroundTask];
143 _debugBackgroundTask = UIBackgroundTaskInvalid;
144 }
145 FML_LOG(WARNING)
146 << "\nThe OS has terminated the Flutter debug connection for being "
147 "inactive in the background for too long.\n\n"
148 "There are no errors with your Flutter application.\n\n"
149 "To reconnect, launch your application again via 'flutter run'";
150 }];
151#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
152 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
153 if (!delegate) {
154 continue;
155 }
156 if ([delegate respondsToSelector:@selector(applicationDidEnterBackground:)]) {
157 [delegate applicationDidEnterBackground:application];
158 }
159 }
160}
161
162- (void)handleWillEnterForeground:(NSNotification*)notification
163 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
164 UIApplication* application = [UIApplication sharedApplication];
165#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
166 if (_debugBackgroundTask != UIBackgroundTaskInvalid) {
167 [application endBackgroundTask:_debugBackgroundTask];
168 _debugBackgroundTask = UIBackgroundTaskInvalid;
169 }
170#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
171 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
172 if (!delegate) {
173 continue;
174 }
175 if ([delegate respondsToSelector:@selector(applicationWillEnterForeground:)]) {
176 [delegate applicationWillEnterForeground:application];
177 }
178 }
179}
180
181- (void)handleWillResignActive:(NSNotification*)notification
182 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
183 UIApplication* application = [UIApplication sharedApplication];
184 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
185 if (!delegate) {
186 continue;
187 }
188 if ([delegate respondsToSelector:@selector(applicationWillResignActive:)]) {
189 [delegate applicationWillResignActive:application];
190 }
191 }
192}
193
194- (void)handleDidBecomeActive:(NSNotification*)notification
195 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
196 UIApplication* application = [UIApplication sharedApplication];
197 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
198 if (!delegate) {
199 continue;
200 }
201 if ([delegate respondsToSelector:@selector(applicationDidBecomeActive:)]) {
202 [delegate applicationDidBecomeActive:application];
203 }
204 }
205}
206
207- (void)handleWillTerminate:(NSNotification*)notification
208 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
209 UIApplication* application = [UIApplication sharedApplication];
210 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
211 if (!delegate) {
212 continue;
213 }
214 if ([delegate respondsToSelector:@selector(applicationWillTerminate:)]) {
215 [delegate applicationWillTerminate:application];
216 }
217 }
218}
219
220#pragma GCC diagnostic push
221#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
222- (void)application:(UIApplication*)application
223 didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
224 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
225 if (!delegate) {
226 continue;
227 }
228 if ([delegate respondsToSelector:_cmd]) {
229 [delegate application:application didRegisterUserNotificationSettings:notificationSettings];
230 }
231 }
232}
233#pragma GCC diagnostic pop
234
235- (void)application:(UIApplication*)application
236 didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
237 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
238 if (!delegate) {
239 continue;
240 }
241 if ([delegate respondsToSelector:_cmd]) {
242 [delegate application:application
244 }
245 }
246}
247
248- (void)application:(UIApplication*)application
249 didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
250 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
251 if (!delegate) {
252 continue;
253 }
254 if ([delegate respondsToSelector:_cmd]) {
256 }
257 }
258}
259
260- (void)application:(UIApplication*)application
261 didReceiveRemoteNotification:(NSDictionary*)userInfo
262 fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
263 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
264 if (!delegate) {
265 continue;
266 }
267 if ([delegate respondsToSelector:_cmd]) {
268 if ([delegate application:application
269 didReceiveRemoteNotification:userInfo
270 fetchCompletionHandler:completionHandler]) {
271 return;
272 }
273 }
274 }
275}
276
277#pragma GCC diagnostic push
278#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
279- (void)application:(UIApplication*)application
280 didReceiveLocalNotification:(UILocalNotification*)notification {
281 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
282 if (!delegate) {
283 continue;
284 }
285 if ([delegate respondsToSelector:_cmd]) {
286 [delegate application:application didReceiveLocalNotification:notification];
287 }
288 }
289}
290#pragma GCC diagnostic pop
291
292- (void)userNotificationCenter:(UNUserNotificationCenter*)center
293 willPresentNotification:(UNNotification*)notification
294 withCompletionHandler:
295 (void (^)(UNNotificationPresentationOptions options))completionHandler {
296 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
297 if ([delegate respondsToSelector:_cmd]) {
298 [delegate userNotificationCenter:center
299 willPresentNotification:notification
300 withCompletionHandler:completionHandler];
301 }
302 }
303}
304
305- (void)userNotificationCenter:(UNUserNotificationCenter*)center
306 didReceiveNotificationResponse:(UNNotificationResponse*)response
307 withCompletionHandler:(void (^)(void))completionHandler {
308 for (id<FlutterApplicationLifeCycleDelegate> delegate in _delegates) {
309 if ([delegate respondsToSelector:_cmd]) {
310 [delegate userNotificationCenter:center
311 didReceiveNotificationResponse:response
312 withCompletionHandler:completionHandler];
313 }
314 }
315}
316
317- (BOOL)application:(UIApplication*)application
318 openURL:(NSURL*)url
319 options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
320 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
321 if (!delegate) {
322 continue;
323 }
324 if ([delegate respondsToSelector:_cmd]) {
325 if ([delegate application:application openURL:url options:options]) {
326 return YES;
327 }
328 }
329 }
330 return NO;
331}
332
333- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
334 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
335 if (!delegate) {
336 continue;
337 }
338 if ([delegate respondsToSelector:_cmd]) {
339 if ([delegate application:application handleOpenURL:url]) {
340 return YES;
341 }
342 }
343 }
344 return NO;
345}
346
347- (BOOL)application:(UIApplication*)application
348 openURL:(NSURL*)url
349 sourceApplication:(NSString*)sourceApplication
350 annotation:(id)annotation {
351 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
352 if (!delegate) {
353 continue;
354 }
355 if ([delegate respondsToSelector:_cmd]) {
356 if ([delegate application:application
357 openURL:url
358 sourceApplication:sourceApplication
359 annotation:annotation]) {
360 return YES;
361 }
362 }
363 }
364 return NO;
365}
366
367- (void)application:(UIApplication*)application
368 performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
369 completionHandler:(void (^)(BOOL succeeded))completionHandler {
370 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
371 if (!delegate) {
372 continue;
373 }
374 if ([delegate respondsToSelector:_cmd]) {
375 if ([delegate application:application
376 performActionForShortcutItem:shortcutItem
377 completionHandler:completionHandler]) {
378 return;
379 }
380 }
381 }
382}
383
384- (BOOL)application:(UIApplication*)application
385 handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
386 completionHandler:(nonnull void (^)())completionHandler {
387 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
388 if (!delegate) {
389 continue;
390 }
391 if ([delegate respondsToSelector:_cmd]) {
392 if ([delegate application:application
393 handleEventsForBackgroundURLSession:identifier
394 completionHandler:completionHandler]) {
395 return YES;
396 }
397 }
398 }
399 return NO;
400}
401
402- (BOOL)application:(UIApplication*)application
403 performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
404 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
405 if (!delegate) {
406 continue;
407 }
408 if ([delegate respondsToSelector:_cmd]) {
409 if ([delegate application:application performFetchWithCompletionHandler:completionHandler]) {
410 return YES;
411 }
412 }
413 }
414 return NO;
415}
416
417- (BOOL)application:(UIApplication*)application
418 continueUserActivity:(NSUserActivity*)userActivity
419 restorationHandler:(void (^)(NSArray*))restorationHandler {
420 for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
421 if (!delegate) {
422 continue;
423 }
424 if ([delegate respondsToSelector:_cmd]) {
425 if ([delegate application:application
426 continueUserActivity:userActivity
427 restorationHandler:restorationHandler]) {
428 return YES;
429 }
430 }
431 }
432 return NO;
433}
434@end
const char * options
static const SEL kSelectorsHandledByPlugins[]
NSPointerArray * _delegates
static FLUTTER_ASSERT_ARC const char * kCallbackCacheSubDir
int count
Definition: FontMgrTest.cpp:50
static SkString identifier(const FontFamilyDesc &family, const FontDesc &font)
GAsyncResult * result
void setCachePath:(NSString *path)
double x
static bool init()
std::string JoinPaths(std::initializer_list< std::string > components)
Definition: paths.cc:14
void application:didFailToRegisterForRemoteNotificationsWithError:(UIApplication *application,[didFailToRegisterForRemoteNotificationsWithError] NSError *error)
void application:didReceiveLocalNotification:(UIApplication *application,[didReceiveLocalNotification] "See -[UIApplicationDelegate application:didReceiveLocalNotification:] deprecation", ios(4.0, 10.0) API_DEPRECATED)
void applicationDidBecomeActive:(UIApplication *application)
void application:didRegisterForRemoteNotificationsWithDeviceToken:(UIApplication *application,[didRegisterForRemoteNotificationsWithDeviceToken] NSData *deviceToken)
void applicationWillTerminate:(UIApplication *application)
void application:didRegisterUserNotificationSettings:(UIApplication *application,[didRegisterUserNotificationSettings] "See -[UIApplicationDelegate application:didRegisterUserNotificationSettings:] deprecation", ios(8.0, 10.0) API_DEPRECATED)
void applicationDidEnterBackground:(UIApplication *application)
void applicationWillEnterForeground:(UIApplication *application)
void applicationWillResignActive:(UIApplication *application)
int BOOL
Definition: windows_types.h:37