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