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