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