| 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:(void (^)(NSArray<id<UIUserActivityRestoring>>* __nullable
228 restorableObjects))restorationHandler {
229 if ([self.lifeCycleDelegate application:application
230 continueUserActivity:userActivity
231 restorationHandler:restorationHandler]) {
232 return YES;
233 }
234
235 return [self handleOpenURL:userActivity.webpageURL options:@{} relayToSystemIfUnhandled:YES];
236 }
237
238 #pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController
239
240 - (NSObject<FlutterPluginRegistrant>*)pluginRegistrant {
241 if (_weakRegistrant) {
242 return _weakRegistrant;
243 }
244 if (_strongRegistrant) {
245 return _strongRegistrant;
246 }
247 return nil;
248 }
249
250 - (void)setPluginRegistrant:(NSObject<FlutterPluginRegistrant>*)pluginRegistrant {
251 if (pluginRegistrant == (id)self) {
252 _weakRegistrant = pluginRegistrant;
253 _strongRegistrant = nil;
254 } else {
255 _weakRegistrant = nil;
256 _strongRegistrant = pluginRegistrant;
257 }
258 }
259
260 - (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
261 FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
262 if (flutterRootViewController) {
263 return [[flutterRootViewController pluginRegistry] registrarForPlugin:pluginKey];
264 }
265 return [self.launchEngine.engine registrarForPlugin:pluginKey];
266 }
267
268 - (BOOL)hasPlugin:(NSString*)pluginKey {
269 FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
270 if (flutterRootViewController) {
271 return [[flutterRootViewController pluginRegistry] hasPlugin:pluginKey];
272 }
273 return [self.launchEngine.engine hasPlugin:pluginKey];
274 }
275
276 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
277 FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
278 if (flutterRootViewController) {
279 return [[flutterRootViewController pluginRegistry] valuePublishedByPlugin:pluginKey];
280 }
281 return [self.launchEngine.engine valuePublishedByPlugin:pluginKey];
282 }
283
284 #pragma mark - Selectors handling
285
286 - (void)addApplicationLifeCycleDelegate:(NSObject<FlutterApplicationLifeCycleDelegate>*)delegate {
287 [self.lifeCycleDelegate addDelegate:delegate];
288 }
289
290 #pragma mark - UIApplicationDelegate method dynamic implementation
291
292 - (BOOL)respondsToSelector:(SEL)selector {
293 if ([self.lifeCycleDelegate isSelectorAddedDynamically:selector]) {
294 return [self delegateRespondsSelectorToPlugins:selector];
295 }
296 return [super respondsToSelector:selector];
297 }
298
299 - (BOOL)delegateRespondsSelectorToPlugins:(SEL)selector {
300 if ([self.lifeCycleDelegate hasPluginThatRespondsToSelector:selector]) {
301 return [self.lifeCycleDelegate respondsToSelector:selector];
302 } else {
303 return NO;
304 }
305 }
306
307 - (id)forwardingTargetForSelector:(SEL)aSelector {
308 if ([self.lifeCycleDelegate isSelectorAddedDynamically:aSelector]) {
309 [self logCapabilityConfigurationWarningIfNeeded:aSelector];
310 return self.lifeCycleDelegate;
311 }
312 return [super forwardingTargetForSelector:aSelector];
313 }
314
315 // Mimic the logging from Apple when the capability is not set for the selectors.
316 // However the difference is that Apple logs these message when the app launches, we only
317 // log it when the method is invoked. We can possibly also log it when the app launches, but
318 // it will cause an additional scan over all the plugins.
319 - (void)logCapabilityConfigurationWarningIfNeeded:(SEL)selector {
320 NSArray* backgroundModesArray =
321 [[NSBundle mainBundle] objectForInfoDictionaryKey:kUIBackgroundMode];
322 NSSet* backgroundModesSet = [[NSSet alloc] initWithArray:backgroundModesArray];
323 if (selector == @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)) {
324 if (![backgroundModesSet containsObject:kRemoteNotificationCapabitiliy]) {
325 NSLog(
326 @"You've implemented -[<UIApplicationDelegate> "
327 @"application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need "
328 @"to add \"remote-notification\" to the list of your supported UIBackgroundModes in your "
329 @"Info.plist.");
330 }
331 } else if (selector == @selector(application:performFetchWithCompletionHandler:)) {
332 if (![backgroundModesSet containsObject:kBackgroundFetchCapatibility]) {
333 NSLog(@"You've implemented -[<UIApplicationDelegate> "
334 @"application:performFetchWithCompletionHandler:], but you still need to add \"fetch\" "
335 @"to the list of your supported UIBackgroundModes in your Info.plist.");
336 }
337 }
338 }
339
340 #pragma mark - State Restoration
341
342 - (BOOL)application:(UIApplication*)application shouldSaveApplicationState:(NSCoder*)coder {
343 [coder encodeInt64:self.lastAppModificationTime forKey:kRestorationStateAppModificationKey];
344 return YES;
345 }
346
347 - (BOOL)application:(UIApplication*)application shouldRestoreApplicationState:(NSCoder*)coder {
348 int64_t stateDate = [coder decodeInt64ForKey:kRestorationStateAppModificationKey];
349 return self.lastAppModificationTime == stateDate;
350 }
351
352 - (BOOL)application:(UIApplication*)application shouldSaveSecureApplicationState:(NSCoder*)coder {
353 [coder encodeInt64:self.lastAppModificationTime forKey:kRestorationStateAppModificationKey];
354 return YES;
355 }
356
357 - (BOOL)application:(UIApplication*)application
358 shouldRestoreSecureApplicationState:(NSCoder*)coder {
359 int64_t stateDate = [coder decodeInt64ForKey:kRestorationStateAppModificationKey];
360 return self.lastAppModificationTime == stateDate;
361 }
362
363 - (int64_t)lastAppModificationTime {
364 NSDate* fileDate;
365 NSError* error = nil;
366 [[[NSBundle mainBundle] executableURL] getResourceValue:&fileDate
367 forKey:NSURLContentModificationDateKey
368 error:&error];
369 NSAssert(error == nil, @"Cannot obtain modification date of main bundle: %@", error);
370 return [fileDate timeIntervalSince1970];
371 }
372
373 @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:450
FlutterPluginRegistrar-p
Definition: FlutterPlugin.h:283