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