Flutter Engine
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 #include "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/FlutterPluginAppLifeCycleDelegate_internal.h"
11 
12 static NSString* kUIBackgroundMode = @"UIBackgroundModes";
13 static NSString* kRemoteNotificationCapabitiliy = @"remote-notification";
14 static NSString* kBackgroundFetchCapatibility = @"fetch";
15 
16 @implementation FlutterAppDelegate {
17  FlutterPluginAppLifeCycleDelegate* _lifeCycleDelegate;
18 }
19 
20 - (instancetype)init {
21  if (self = [super init]) {
22  _lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
23  }
24  return self;
25 }
26 
27 - (void)dealloc {
28  [_lifeCycleDelegate release];
29  [super dealloc];
30 }
31 
32 - (BOOL)application:(UIApplication*)application
33  willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
34  return [_lifeCycleDelegate application:application willFinishLaunchingWithOptions:launchOptions];
35 }
36 
37 - (BOOL)application:(UIApplication*)application
38  didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
39  return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
40 }
41 
42 // Returns the key window's rootViewController, if it's a FlutterViewController.
43 // Otherwise, returns nil.
44 + (FlutterViewController*)rootFlutterViewController {
45  UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
46  if ([viewController isKindOfClass:[FlutterViewController class]]) {
47  return (FlutterViewController*)viewController;
48  }
49  return nil;
50 }
51 
52 // Do not remove, some clients may be calling these via `super`.
53 - (void)applicationDidEnterBackground:(UIApplication*)application {
54 }
55 
56 // Do not remove, some clients may be calling these via `super`.
57 - (void)applicationWillEnterForeground:(UIApplication*)application {
58 }
59 
60 // Do not remove, some clients may be calling these via `super`.
61 - (void)applicationWillResignActive:(UIApplication*)application {
62 }
63 
64 // Do not remove, some clients may be calling these via `super`.
65 - (void)applicationDidBecomeActive:(UIApplication*)application {
66 }
67 
68 // Do not remove, some clients may be calling these via `super`.
69 - (void)applicationWillTerminate:(UIApplication*)application {
70 }
71 
72 #pragma GCC diagnostic push
73 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
74 - (void)application:(UIApplication*)application
75  didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
76  [_lifeCycleDelegate application:application
77  didRegisterUserNotificationSettings:notificationSettings];
78 }
79 #pragma GCC diagnostic pop
80 
81 - (void)application:(UIApplication*)application
82  didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
83  [_lifeCycleDelegate application:application
84  didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
85 }
86 
87 #pragma GCC diagnostic push
88 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
89 - (void)application:(UIApplication*)application
90  didReceiveLocalNotification:(UILocalNotification*)notification {
91  [_lifeCycleDelegate application:application didReceiveLocalNotification:notification];
92 }
93 #pragma GCC diagnostic pop
94 
95 - (void)userNotificationCenter:(UNUserNotificationCenter*)center
96  willPresentNotification:(UNNotification*)notification
97  withCompletionHandler:
98  (void (^)(UNNotificationPresentationOptions options))completionHandler
99  NS_AVAILABLE_IOS(10_0) {
100  if (@available(iOS 10.0, *)) {
101  if ([_lifeCycleDelegate respondsToSelector:_cmd]) {
102  [_lifeCycleDelegate userNotificationCenter:center
103  willPresentNotification:notification
104  withCompletionHandler:completionHandler];
105  }
106  }
107 }
108 
109 /**
110  * Calls all plugins registered for `UNUserNotificationCenterDelegate` callbacks.
111  */
112 - (void)userNotificationCenter:(UNUserNotificationCenter*)center
113  didReceiveNotificationResponse:(UNNotificationResponse*)response
114  withCompletionHandler:(void (^)(void))completionHandler NS_AVAILABLE_IOS(10_0) {
115  if (@available(iOS 10.0, *)) {
116  if ([_lifeCycleDelegate respondsToSelector:_cmd]) {
117  [_lifeCycleDelegate userNotificationCenter:center
118  didReceiveNotificationResponse:response
119  withCompletionHandler:completionHandler];
120  }
121  }
122 }
123 
124 - (BOOL)application:(UIApplication*)application
125  openURL:(NSURL*)url
126  options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
127  return [_lifeCycleDelegate application:application openURL:url options:options];
128 }
129 
130 - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
131  return [_lifeCycleDelegate application:application handleOpenURL:url];
132 }
133 
134 - (BOOL)application:(UIApplication*)application
135  openURL:(NSURL*)url
136  sourceApplication:(NSString*)sourceApplication
137  annotation:(id)annotation {
138  return [_lifeCycleDelegate application:application
139  openURL:url
140  sourceApplication:sourceApplication
141  annotation:annotation];
142 }
143 
144 - (void)application:(UIApplication*)application
145  performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
146  completionHandler:(void (^)(BOOL succeeded))completionHandler NS_AVAILABLE_IOS(9_0) {
147  [_lifeCycleDelegate application:application
148  performActionForShortcutItem:shortcutItem
149  completionHandler:completionHandler];
150 }
151 
152 - (void)application:(UIApplication*)application
153  handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
154  completionHandler:(nonnull void (^)())completionHandler {
155  [_lifeCycleDelegate application:application
156  handleEventsForBackgroundURLSession:identifier
157  completionHandler:completionHandler];
158 }
159 
160 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000
161 - (BOOL)application:(UIApplication*)application
162  continueUserActivity:(NSUserActivity*)userActivity
163  restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>>* __nullable
164  restorableObjects))restorationHandler {
165 #else
166 - (BOOL)application:(UIApplication*)application
167  continueUserActivity:(NSUserActivity*)userActivity
168  restorationHandler:(void (^)(NSArray* __nullable restorableObjects))restorationHandler {
169 #endif
170  return [_lifeCycleDelegate application:application
171  continueUserActivity:userActivity
172  restorationHandler:restorationHandler];
173 }
174 
175 #pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController
176 
177 - (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
178  UIViewController* rootViewController = _window.rootViewController;
179  if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
180  return
181  [[(FlutterViewController*)rootViewController pluginRegistry] registrarForPlugin:pluginKey];
182  }
183  return nil;
184 }
185 
186 - (BOOL)hasPlugin:(NSString*)pluginKey {
187  UIViewController* rootViewController = _window.rootViewController;
188  if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
189  return [[(FlutterViewController*)rootViewController pluginRegistry] hasPlugin:pluginKey];
190  }
191  return false;
192 }
193 
194 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
195  UIViewController* rootViewController = _window.rootViewController;
196  if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
197  return [[(FlutterViewController*)rootViewController pluginRegistry]
198  valuePublishedByPlugin:pluginKey];
199  }
200  return nil;
201 }
202 
203 #pragma mark - Selectors handling
204 
205 - (void)addApplicationLifeCycleDelegate:(NSObject<FlutterApplicationLifeCycleDelegate>*)delegate {
206  [_lifeCycleDelegate addDelegate:delegate];
207 }
208 
209 #pragma mark - UIApplicationDelegate method dynamic implementation
210 
211 - (BOOL)respondsToSelector:(SEL)selector {
212  if ([_lifeCycleDelegate isSelectorAddedDynamically:selector]) {
213  return [self delegateRespondsSelectorToPlugins:selector];
214  }
215  return [super respondsToSelector:selector];
216 }
217 
218 - (BOOL)delegateRespondsSelectorToPlugins:(SEL)selector {
219  if ([_lifeCycleDelegate hasPluginThatRespondsToSelector:selector]) {
220  return [_lifeCycleDelegate respondsToSelector:selector];
221  } else {
222  return NO;
223  }
224 }
225 
226 - (id)forwardingTargetForSelector:(SEL)aSelector {
227  if ([_lifeCycleDelegate isSelectorAddedDynamically:aSelector]) {
228  [self logCapabilityConfigurationWarningIfNeeded:aSelector];
229  return _lifeCycleDelegate;
230  }
231  return [super forwardingTargetForSelector:aSelector];
232 }
233 
234 // Mimic the logging from Apple when the capability is not set for the selectors.
235 // However the difference is that Apple logs these message when the app launches, we only
236 // log it when the method is invoked. We can possibly also log it when the app launches, but
237 // it will cause an additional scan over all the plugins.
238 - (void)logCapabilityConfigurationWarningIfNeeded:(SEL)selector {
239  NSArray* backgroundModesArray =
240  [[NSBundle mainBundle] objectForInfoDictionaryKey:kUIBackgroundMode];
241  NSSet* backgroundModesSet = [[[NSSet alloc] initWithArray:backgroundModesArray] autorelease];
242  if (selector == @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)) {
243  if (![backgroundModesSet containsObject:kRemoteNotificationCapabitiliy]) {
244  NSLog(
245  @"You've implemented -[<UIApplicationDelegate> "
246  @"application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need "
247  @"to add \"remote-notification\" to the list of your supported UIBackgroundModes in your "
248  @"Info.plist.");
249  }
250  } else if (selector == @selector(application:performFetchWithCompletionHandler:)) {
251  if (![backgroundModesSet containsObject:kBackgroundFetchCapatibility]) {
252  NSLog(@"You've implemented -[<UIApplicationDelegate> "
253  @"application:performFetchWithCompletionHandler:], but you still need to add \"fetch\" "
254  @"to the list of your supported UIBackgroundModes in your Info.plist.");
255  }
256  }
257 }
258 
259 @end
static NSString * kUIBackgroundMode
static NSString * kRemoteNotificationCapabitiliy
static NSString * kBackgroundFetchCapatibility
int32_t id