| Flutter iOS Embedder |

FlutterAppDelegate.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/FlutterAppDelegate.h"
6
7 #import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
8 #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h"
9 #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
10 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h"
11 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
12 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterLaunchEngine.h"
13 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h"
14 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSharedApplication.h"
15 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
16
17 FLUTTER_ASSERT_ARC
18
19 static NSString* const kUIBackgroundMode = @"UIBackgroundModes";
20 static NSString* const kRemoteNotificationCapabitiliy = @"remote-notification";
21 static NSString* const kBackgroundFetchCapatibility = @"fetch";
22 static NSString* const kRestorationStateAppModificationKey = @"mod-date";
23
24 @interface FlutterAppDelegate () {
25 __weak NSObject<FlutterPluginRegistrant>* _weakRegistrant;
26 NSObject<FlutterPluginRegistrant>* _strongRegistrant;
27 }
28 @property(nonatomic, copy) FlutterViewController* (^rootFlutterViewControllerGetter)(void);
29 @property(nonatomic, strong) FlutterPluginAppLifeCycleDelegate* lifeCycleDelegate;
30 @property(nonatomic, strong) FlutterLaunchEngine* launchEngine;
31 @end
32
33 @implementation FlutterAppDelegate
34
35 - (instancetype)init {
36 if (self = [super init]) {
37 _lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
38 _launchEngine = [[FlutterLaunchEngine alloc] init];
39 }
40 return self;
41 }
42
43 - (nullable FlutterEngine*)takeLaunchEngine {
44 return [self.launchEngine takeEngine];
45 }
46
47 - (BOOL)application:(UIApplication*)application
48 willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
49 return [self.lifeCycleDelegate application:application
50 willFinishLaunchingWithOptions:launchOptions];
51 }
52
53 - (BOOL)application:(UIApplication*)application
54 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
55 return [self.lifeCycleDelegate application:application
56 didFinishLaunchingWithOptions:launchOptions];
57 }
58
59 // Returns the key window's rootViewController, if it's a FlutterViewController.
60 // Otherwise, returns nil.
61 - (FlutterViewController*)rootFlutterViewController {
62 if (_rootFlutterViewControllerGetter != nil) {
63 return _rootFlutterViewControllerGetter();
64 }
65 UIViewController* rootViewController = _window.rootViewController;
66 if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
67 return (FlutterViewController*)rootViewController;
68 }
69 return nil;
70 }
71
72 // Do not remove, some clients may be calling these via `super`.
73 - (void)applicationDidEnterBackground:(UIApplication*)application {
74 }
75
76 // Do not remove, some clients may be calling these via `super`.
77 - (void)applicationWillEnterForeground:(UIApplication*)application {
78 }
79
80 // Do not remove, some clients may be calling these via `super`.
81 - (void)applicationWillResignActive:(UIApplication*)application {
82 }
83
84 // Do not remove, some clients may be calling these via `super`.
85 - (void)applicationDidBecomeActive:(UIApplication*)application {
86 }
87
88 // Do not remove, some clients may be calling these via `super`.
89 - (void)applicationWillTerminate:(UIApplication*)application {
90 }
91
92 #pragma GCC diagnostic push
93 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
94 - (void)application:(UIApplication*)application
95 didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
96 [self.lifeCycleDelegate application:application
97 didRegisterUserNotificationSettings:notificationSettings];
98 }
99 #pragma GCC diagnostic pop
100
101 - (void)application:(UIApplication*)application
102 didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
103 [self.lifeCycleDelegate application:application
104 didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
105 }
106
107 - (void)application:(UIApplication*)application
108 didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
109 [self.lifeCycleDelegate application:application
110 didFailToRegisterForRemoteNotificationsWithError:error];
111 }
112
113 #pragma GCC diagnostic push
114 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
115 - (void)application:(UIApplication*)application
116 didReceiveLocalNotification:(UILocalNotification*)notification {
117 [self.lifeCycleDelegate application:application didReceiveLocalNotification:notification];
118 }
119 #pragma GCC diagnostic pop
120
121 - (void)userNotificationCenter:(UNUserNotificationCenter*)center
122 willPresentNotification:(UNNotification*)notification
123 withCompletionHandler:
124 (void (^)(UNNotificationPresentationOptions options))completionHandler {
125 if ([self.lifeCycleDelegate respondsToSelector:_cmd]) {
126 [self.lifeCycleDelegate userNotificationCenter:center
127 willPresentNotification:notification
128 withCompletionHandler:completionHandler];
129 }
130 }
131
132 /**
133 * Calls all plugins registered for `UNUserNotificationCenterDelegate` callbacks.
134 */
135 - (void)userNotificationCenter:(UNUserNotificationCenter*)center
136 didReceiveNotificationResponse:(UNNotificationResponse*)response
137 withCompletionHandler:(void (^)(void))completionHandler {
138 if ([self.lifeCycleDelegate respondsToSelector:_cmd]) {
139 [self.lifeCycleDelegate userNotificationCenter:center
140 didReceiveNotificationResponse:response
141 withCompletionHandler:completionHandler];
142 }
143 }
144
145 - (BOOL)isFlutterDeepLinkingEnabled {
146 NSNumber* isDeepLinkingEnabled =
147 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"];
148 // if not set, return YES
149 return isDeepLinkingEnabled ? [isDeepLinkingEnabled boolValue] : YES;
150 }
151
152 // This method is called when opening an URL with custom schemes.
153 - (BOOL)application:(UIApplication*)application
154 openURL:(NSURL*)url
155 options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
156 if ([self.lifeCycleDelegate application:application openURL:url options:options]) {
157 return YES;
158 }
159
160 // Relaying to the system here will case an infinite loop, so we don't do it here.
161 return [self handleOpenURL:url options:options relayToSystemIfUnhandled:NO];
162 }
163
164 // Helper function for opening an URL, either with a custom scheme or a http/https scheme.
165 - (BOOL)handleOpenURL:(NSURL*)url
166 options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options
167 relayToSystemIfUnhandled:(BOOL)throwBack {
168 UIApplication* flutterApplication = FlutterSharedApplication.application;
169 if (flutterApplication == nil) {
170 return NO;
171 }
172 if (![self isFlutterDeepLinkingEnabled]) {
173 return NO;
174 }
175
176 FlutterViewController* flutterViewController = [self rootFlutterViewController];
177 if (flutterViewController) {
178 [flutterViewController sendDeepLinkToFramework:url
179 completionHandler:^(BOOL success) {
180 if (!success && throwBack) {
181 // throw it back to iOS
182 [flutterApplication openURL:url
183 options:@{}
184 completionHandler:nil];
185 }
186 }];
187 } else {
188 [FlutterLogger logError:@"Attempting to open an URL without a Flutter RootViewController."];
189 return NO;
190 }
191 return YES;
192 }
193
194 - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
195 return [self.lifeCycleDelegate application:application handleOpenURL:url];
196 }
197
198 - (BOOL)application:(UIApplication*)application
199 openURL:(NSURL*)url
200 sourceApplication:(NSString*)sourceApplication
201 annotation:(id)annotation {
202 return [self.lifeCycleDelegate application:application
203 openURL:url
204 sourceApplication:sourceApplication
205 annotation:annotation];
206 }
207
208 - (void)application:(UIApplication*)application
209 performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
210 completionHandler:(void (^)(BOOL succeeded))completionHandler {
211 [self.lifeCycleDelegate application:application
212 performActionForShortcutItem:shortcutItem
213 completionHandler:completionHandler];
214 }
215
216 - (void)application:(UIApplication*)application
217 handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
218 completionHandler:(nonnull void (^)())completionHandler {
219 [self.lifeCycleDelegate application:application
220 handleEventsForBackgroundURLSession:identifier
221 completionHandler:completionHandler];
222 }
223
224 // This method is called when opening an URL with a http/https scheme.
225 - (BOOL)application:(UIApplication*)application
226 continueUserActivity:(NSUserActivity*)userActivity
227 restorationHandler:
228 (void (^)(NSArray<id<UIUserActivityRestoring>>* __nullable restorableObjects))
229 restorationHandler {
230 if ([self.lifeCycleDelegate application:application
231 continueUserActivity:userActivity
232 restorationHandler:restorationHandler]) {
233 return YES;
234 }
235
236 return [self handleOpenURL:userActivity.webpageURL options:@{} relayToSystemIfUnhandled:YES];
237 }
238
239 #pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController
240
241 - (NSObject<FlutterPluginRegistrant>*)pluginRegistrant {
242 if (_weakRegistrant) {
243 return _weakRegistrant;
244 }
245 if (_strongRegistrant) {
246 return _strongRegistrant;
247 }
248 return nil;
249 }
250
251 - (void)setPluginRegistrant:(NSObject<FlutterPluginRegistrant>*)pluginRegistrant {
252 if (pluginRegistrant == (id)self) {
253 _weakRegistrant = pluginRegistrant;
254 _strongRegistrant = nil;
255 } else {
256 _weakRegistrant = nil;
257 _strongRegistrant = pluginRegistrant;
258 }
259 }
260
261 - (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
262 FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
263 if (flutterRootViewController) {
264 return [[flutterRootViewController pluginRegistry] registrarForPlugin:pluginKey];
265 }
266 return [self.launchEngine.engine registrarForPlugin:pluginKey];
267 }
268
269 - (BOOL)hasPlugin:(NSString*)pluginKey {
270 FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
271 if (flutterRootViewController) {
272 return [[flutterRootViewController pluginRegistry] hasPlugin:pluginKey];
273 }
274 return [self.launchEngine.engine hasPlugin:pluginKey];
275 }
276
277 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
278 FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
279 if (flutterRootViewController) {
280 return [[flutterRootViewController pluginRegistry] valuePublishedByPlugin:pluginKey];
281 }
282 return [self.launchEngine.engine valuePublishedByPlugin:pluginKey];
283 }
284
285 #pragma mark - Selectors handling
286
287 - (void)addApplicationLifeCycleDelegate:(NSObject<FlutterApplicationLifeCycleDelegate>*)delegate {
288 [self.lifeCycleDelegate addDelegate:delegate];
289 }
290
291 #pragma mark - UIApplicationDelegate method dynamic implementation
292
293 - (BOOL)respondsToSelector:(SEL)selector {
294 if ([self.lifeCycleDelegate isSelectorAddedDynamically:selector]) {
295 return [self delegateRespondsSelectorToPlugins:selector];
296 }
297 return [super respondsToSelector:selector];
298 }
299
300 - (BOOL)delegateRespondsSelectorToPlugins:(SEL)selector {
301 if ([self.lifeCycleDelegate hasPluginThatRespondsToSelector:selector]) {
302 return [self.lifeCycleDelegate respondsToSelector:selector];
303 } else {
304 return NO;
305 }
306 }
307
308 - (id)forwardingTargetForSelector:(SEL)aSelector {
309 if ([self.lifeCycleDelegate isSelectorAddedDynamically:aSelector]) {
310 [self logCapabilityConfigurationWarningIfNeeded:aSelector];
311 return self.lifeCycleDelegate;
312 }
313 return [super forwardingTargetForSelector:aSelector];
314 }
315
316 // Mimic the logging from Apple when the capability is not set for the selectors.
317 // However the difference is that Apple logs these message when the app launches, we only
318 // log it when the method is invoked. We can possibly also log it when the app launches, but
319 // it will cause an additional scan over all the plugins.
320 - (void)logCapabilityConfigurationWarningIfNeeded:(SEL)selector {
321 NSArray* backgroundModesArray =
322 [[NSBundle mainBundle] objectForInfoDictionaryKey:kUIBackgroundMode];
323 NSSet* backgroundModesSet = [[NSSet alloc] initWithArray:backgroundModesArray];
324 if (selector == @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)) {
325 if (![backgroundModesSet containsObject:kRemoteNotificationCapabitiliy]) {
326 NSLog(
327 @"You've implemented -[<UIApplicationDelegate> "
328 @"application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need "
329 @"to add \"remote-notification\" to the list of your supported UIBackgroundModes in your "
330 @"Info.plist.");
331 }
332 } else if (selector == @selector(application:performFetchWithCompletionHandler:)) {
333 if (![backgroundModesSet containsObject:kBackgroundFetchCapatibility]) {
334 NSLog(@"You've implemented -[<UIApplicationDelegate> "
335 @"application:performFetchWithCompletionHandler:], but you still need to add \"fetch\" "
336 @"to the list of your supported UIBackgroundModes in your Info.plist.");
337 }
338 }
339 }
340
341 #pragma mark - State Restoration
342
343 - (BOOL)application:(UIApplication*)application shouldSaveApplicationState:(NSCoder*)coder {
344 [coder encodeInt64:self.lastAppModificationTime forKey:kRestorationStateAppModificationKey];
345 return YES;
346 }
347
348 - (BOOL)application:(UIApplication*)application shouldRestoreApplicationState:(NSCoder*)coder {
349 int64_t stateDate = [coder decodeInt64ForKey:kRestorationStateAppModificationKey];
350 return self.lastAppModificationTime == stateDate;
351 }
352
353 - (BOOL)application:(UIApplication*)application shouldSaveSecureApplicationState:(NSCoder*)coder {
354 [coder encodeInt64:self.lastAppModificationTime forKey:kRestorationStateAppModificationKey];
355 return YES;
356 }
357
358 - (BOOL)application:(UIApplication*)application
359 shouldRestoreSecureApplicationState:(NSCoder*)coder {
360 int64_t stateDate = [coder decodeInt64ForKey:kRestorationStateAppModificationKey];
361 return self.lastAppModificationTime == stateDate;
362 }
363
364 - (int64_t)lastAppModificationTime {
365 NSDate* fileDate;
366 NSError* error = nil;
367 [[[NSBundle mainBundle] executableURL] getResourceValue:&fileDate
368 forKey:NSURLContentModificationDateKey
369 error:&error];
370 NSAssert(error == nil, @"Cannot obtain modification date of main bundle: %@", error);
371 return [fileDate timeIntervalSince1970];
372 }
373
374 @end
FlutterAppDelegate.h
kRemoteNotificationCapabitiliy
static NSString *const kRemoteNotificationCapabitiliy
Definition: FlutterAppDelegate.mm:20
kUIBackgroundMode
static FLUTTER_ASSERT_ARC NSString *const kUIBackgroundMode
Definition: FlutterAppDelegate.mm:19
kBackgroundFetchCapatibility
static NSString *const kBackgroundFetchCapatibility
Definition: FlutterAppDelegate.mm:21
kRestorationStateAppModificationKey
static NSString *const kRestorationStateAppModificationKey
Definition: FlutterAppDelegate.mm:22
FlutterAppDelegate_Test.h
FlutterEngine_Internal.h
FlutterLaunchEngine.h
FlutterPluginAppLifeCycleDelegate.h
FlutterPluginAppLifeCycleDelegate_internal.h
FlutterSharedApplication.h
FlutterViewController.h
FlutterViewController_Internal.h
FlutterAppDelegate()::_weakRegistrant
__weak NSObject< FlutterPluginRegistrant > * _weakRegistrant
Definition: FlutterAppDelegate.mm:25
FlutterAppDelegate()::_strongRegistrant
NSObject< FlutterPluginRegistrant > * _strongRegistrant
Definition: FlutterAppDelegate.mm:26
FlutterAppDelegate
Definition: FlutterAppDelegate.h:28
FlutterAppDelegate::pluginRegistrant
NSObject< FlutterPluginRegistrant > * pluginRegistrant
Definition: FlutterAppDelegate.h:44
FlutterEngine
Definition: FlutterEngine.h:61
FlutterLaunchEngine
Definition: FlutterLaunchEngine.h:23
FlutterPluginAppLifeCycleDelegate
Definition: FlutterPluginAppLifeCycleDelegate.h:16
FlutterSharedApplication
Definition: FlutterSharedApplication.h:11
FlutterSharedApplication::application
UIApplication * application
Definition: FlutterSharedApplication.h:25
FlutterViewController
Definition: FlutterViewController.h:57
-[FlutterViewController pluginRegistry]
id< FlutterPluginRegistry > pluginRegistry()
Definition: FlutterViewController.mm:2414
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
FlutterApplicationLifeCycleDelegate-p
Definition: FlutterPlugin.h:25
FlutterPluginRegistrant-p
Definition: FlutterPlugin.h:436
FlutterPluginRegistrar-p
Definition: FlutterPlugin.h:283