Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
14static NSString* const kUIBackgroundMode = @"UIBackgroundModes";
15static NSString* const kRemoteNotificationCapabitiliy = @"remote-notification";
16static NSString* const kBackgroundFetchCapatibility = @"fetch";
17static 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)openURL:(NSURL*)url {
138 NSNumber* isDeepLinkingEnabled =
139 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"];
140 if (!isDeepLinkingEnabled.boolValue) {
141 // Not set or NO.
142 return NO;
143 } else {
144 FlutterViewController* flutterViewController = [self rootFlutterViewController];
145 if (flutterViewController) {
146 [flutterViewController.engine
147 waitForFirstFrame:3.0
148 callback:^(BOOL didTimeout) {
149 if (didTimeout) {
150 FML_LOG(ERROR)
151 << "Timeout waiting for the first frame when launching an URL.";
152 } else {
153 [flutterViewController.engine.navigationChannel
154 invokeMethod:@"pushRouteInformation"
155 arguments:@{
156 @"location" : url.absoluteString ?: [NSNull null],
157 }];
158 }
159 }];
160 return YES;
161 } else {
162 FML_LOG(ERROR) << "Attempting to open an URL without a Flutter RootViewController.";
163 return NO;
164 }
165 }
166}
167
168- (BOOL)application:(UIApplication*)application
169 openURL:(NSURL*)url
170 options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
171 if ([_lifeCycleDelegate application:application openURL:url options:options]) {
172 return YES;
173 }
174 return [self openURL:url];
175}
176
177- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
178 return [_lifeCycleDelegate application:application handleOpenURL:url];
179}
180
181- (BOOL)application:(UIApplication*)application
182 openURL:(NSURL*)url
183 sourceApplication:(NSString*)sourceApplication
184 annotation:(id)annotation {
185 return [_lifeCycleDelegate application:application
186 openURL:url
187 sourceApplication:sourceApplication
188 annotation:annotation];
189}
190
191- (void)application:(UIApplication*)application
192 performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
193 completionHandler:(void (^)(BOOL succeeded))completionHandler {
194 [_lifeCycleDelegate application:application
195 performActionForShortcutItem:shortcutItem
196 completionHandler:completionHandler];
197}
198
199- (void)application:(UIApplication*)application
200 handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
201 completionHandler:(nonnull void (^)())completionHandler {
202 [_lifeCycleDelegate application:application
203 handleEventsForBackgroundURLSession:identifier
204 completionHandler:completionHandler];
205}
206
207- (BOOL)application:(UIApplication*)application
208 continueUserActivity:(NSUserActivity*)userActivity
209 restorationHandler:
210 (void (^)(NSArray<id<UIUserActivityRestoring>>* __nullable restorableObjects))
211 restorationHandler {
212 if ([_lifeCycleDelegate application:application
213 continueUserActivity:userActivity
214 restorationHandler:restorationHandler]) {
215 return YES;
216 }
217 return [self openURL:userActivity.webpageURL];
218}
219
220#pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController
221
222- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
223 FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
224 if (flutterRootViewController) {
225 return [[flutterRootViewController pluginRegistry] registrarForPlugin:pluginKey];
226 }
227 return nil;
228}
229
230- (BOOL)hasPlugin:(NSString*)pluginKey {
231 FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
232 if (flutterRootViewController) {
233 return [[flutterRootViewController pluginRegistry] hasPlugin:pluginKey];
234 }
235 return false;
236}
237
238- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
239 FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
240 if (flutterRootViewController) {
241 return [[flutterRootViewController pluginRegistry] valuePublishedByPlugin:pluginKey];
242 }
243 return nil;
244}
245
246#pragma mark - Selectors handling
247
248- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterApplicationLifeCycleDelegate>*)delegate {
249 [_lifeCycleDelegate addDelegate:delegate];
250}
251
252#pragma mark - UIApplicationDelegate method dynamic implementation
253
254- (BOOL)respondsToSelector:(SEL)selector {
255 if ([_lifeCycleDelegate isSelectorAddedDynamically:selector]) {
256 return [self delegateRespondsSelectorToPlugins:selector];
257 }
258 return [super respondsToSelector:selector];
259}
260
261- (BOOL)delegateRespondsSelectorToPlugins:(SEL)selector {
262 if ([_lifeCycleDelegate hasPluginThatRespondsToSelector:selector]) {
263 return [_lifeCycleDelegate respondsToSelector:selector];
264 } else {
265 return NO;
266 }
267}
268
269- (id)forwardingTargetForSelector:(SEL)aSelector {
270 if ([_lifeCycleDelegate isSelectorAddedDynamically:aSelector]) {
271 [self logCapabilityConfigurationWarningIfNeeded:aSelector];
272 return _lifeCycleDelegate;
273 }
274 return [super forwardingTargetForSelector:aSelector];
275}
276
277// Mimic the logging from Apple when the capability is not set for the selectors.
278// However the difference is that Apple logs these message when the app launches, we only
279// log it when the method is invoked. We can possibly also log it when the app launches, but
280// it will cause an additional scan over all the plugins.
281- (void)logCapabilityConfigurationWarningIfNeeded:(SEL)selector {
282 NSArray* backgroundModesArray =
283 [[NSBundle mainBundle] objectForInfoDictionaryKey:kUIBackgroundMode];
284 NSSet* backgroundModesSet = [[[NSSet alloc] initWithArray:backgroundModesArray] autorelease];
285 if (selector == @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)) {
286 if (![backgroundModesSet containsObject:kRemoteNotificationCapabitiliy]) {
287 NSLog(
288 @"You've implemented -[<UIApplicationDelegate> "
289 @"application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need "
290 @"to add \"remote-notification\" to the list of your supported UIBackgroundModes in your "
291 @"Info.plist.");
292 }
293 } else if (selector == @selector(application:performFetchWithCompletionHandler:)) {
294 if (![backgroundModesSet containsObject:kBackgroundFetchCapatibility]) {
295 NSLog(@"You've implemented -[<UIApplicationDelegate> "
296 @"application:performFetchWithCompletionHandler:], but you still need to add \"fetch\" "
297 @"to the list of your supported UIBackgroundModes in your Info.plist.");
298 }
299 }
300}
301
302#pragma mark - State Restoration
303
304- (BOOL)application:(UIApplication*)application shouldSaveApplicationState:(NSCoder*)coder {
305 [coder encodeInt64:self.lastAppModificationTime forKey:kRestorationStateAppModificationKey];
306 return YES;
307}
308
309- (BOOL)application:(UIApplication*)application shouldRestoreApplicationState:(NSCoder*)coder {
310 int64_t stateDate = [coder decodeInt64ForKey:kRestorationStateAppModificationKey];
311 return self.lastAppModificationTime == stateDate;
312}
313
314- (BOOL)application:(UIApplication*)application shouldSaveSecureApplicationState:(NSCoder*)coder {
315 [coder encodeInt64:self.lastAppModificationTime forKey:kRestorationStateAppModificationKey];
316 return YES;
317}
318
319- (BOOL)application:(UIApplication*)application
320 shouldRestoreSecureApplicationState:(NSCoder*)coder {
321 int64_t stateDate = [coder decodeInt64ForKey:kRestorationStateAppModificationKey];
322 return self.lastAppModificationTime == stateDate;
323}
324
325- (int64_t)lastAppModificationTime {
326 NSDate* fileDate;
327 NSError* error = nil;
328 [[[NSBundle mainBundle] executableURL] getResourceValue:&fileDate
329 forKey:NSURLContentModificationDateKey
330 error:&error];
331 NSAssert(error == nil, @"Cannot obtain modification date of main bundle: %@", error);
332 return [fileDate timeIntervalSince1970];
333}
334
335@end
const char * options
const uint8_t uint32_t uint32_t GError ** error
#define FML_LOG(severity)
Definition logging.h:82
id< FlutterPluginRegistry > pluginRegistry()
static NSString *const kRemoteNotificationCapabitiliy
static NSString *const kUIBackgroundMode
static NSString *const kBackgroundFetchCapatibility
static NSString *const kRestorationStateAppModificationKey
Definition copy.py:1
const uintptr_t id
#define ERROR(message)
int BOOL