36@property(nonatomic, assign)
BOOL sceneWillConnectFallbackCalled;
42 if (
self = [super init]) {
43 _flutterManagedEngines = [NSPointerArray weakObjectsPointerArray];
44 _developerManagedEngines = [NSPointerArray weakObjectsPointerArray];
45 _sceneWillConnectFallbackCalled = NO;
46 _sceneWillConnectEventHandledByPlugin = NO;
51#pragma mark - Manual Engine Registration
56 [
self removeFlutterManagedEngine:engine];
63 [
self.developerManagedEngines addPointer:(__bridge void*)engine];
65 [
self compactNSPointerArray:self.developerManagedEngines];
67 engine.manuallyRegisteredToScene = YES;
73 NSUInteger index = [
self.developerManagedEngines.allObjects indexOfObject:engine];
74 if (index != NSNotFound) {
75 [
self.developerManagedEngines removePointerAtIndex:index];
82 return [
self.developerManagedEngines.allObjects containsObject:engine];
85#pragma mark - Automatic Flutter Engine Registration
89 if ([
self.flutterManagedEngines.allObjects containsObject:
engine]) {
94 if (
engine.manuallyRegisteredToScene) {
98 [
self.flutterManagedEngines addPointer:(__bridge void*)engine];
100 [
self compactNSPointerArray:self.flutterManagedEngines];
105 NSUInteger index = [
self.flutterManagedEngines.allObjects indexOfObject:engine];
106 if (index != NSNotFound) {
107 [
self.flutterManagedEngines removePointerAtIndex:index];
113- (void)updateFlutterManagedEnginesInScene:(UIScene*)scene {
117 for (NSUInteger
i = 0;
i <
self.flutterManagedEngines.count;
i++) {
122 [
self.flutterManagedEngines removePointerAtIndex:i];
137 if (actualScene != nil && actualScene != scene) {
138 [
self.flutterManagedEngines removePointerAtIndex:i];
141 if ([actualScene.delegate conformsToProtocol:@protocol(FlutterSceneLifeCycleProvider)]) {
142 id<FlutterSceneLifeCycleProvider> lifeCycleProvider =
143 (id<FlutterSceneLifeCycleProvider>)actualScene.delegate;
144 [lifeCycleProvider.sceneLifeCycleDelegate addFlutterManagedEngine:engine];
151- (NSArray*)allEngines {
152 return [_flutterManagedEngines.allObjects
153 arrayByAddingObjectsFromArray:_developerManagedEngines.allObjects];
163 if ([appDelegate respondsToSelector:@selector(lifeCycleDelegate)]) {
164 id lifecycleDelegate = [appDelegate lifeCycleDelegate];
166 return lifecycleDelegate;
172#pragma mark - Connecting and disconnecting the scene
177 BOOL added = [
self addFlutterManagedEngine:engine];
183 if (
self.connectionOptions != nil) {
185 willConnectToSession:scene.session
187 options:self.connectionOptions];
191- (void)scene:(UIScene*)scene
192 willConnectToSession:(UISceneSession*)session
193 options:(UISceneConnectionOptions*)connectionOptions {
194 self.connectionOptions = connectionOptions;
195 if ([scene.delegate conformsToProtocol:@protocol(UIWindowSceneDelegate)]) {
196 NSObject<UIWindowSceneDelegate>* sceneDelegate =
197 (NSObject<UIWindowSceneDelegate>*)scene.delegate;
201 [
self addFlutterManagedEngine:rootViewController.engine];
205 [
self updateFlutterManagedEnginesInScene:scene];
208 [
self scene:scene willConnectToSession:session flutterEngine:engine options:connectionOptions];
212- (void)scene:(UIScene*)scene
213 willConnectToSession:(UISceneSession*)session
215 options:(UISceneConnectionOptions*)connectionOptions {
218 if (
self.sceneWillConnectEventHandledByPlugin) {
219 availableOptions = nil;
221 BOOL handledByPlugin = [engine.sceneLifeCycleDelegate scene:scene
222 willConnectToSession:session
223 options:availableOptions];
227 if (!handledByPlugin && !
self.sceneWillConnectFallbackCalled) {
228 self.sceneWillConnectFallbackCalled = YES;
230 handledByPlugin = YES;
233 if (handledByPlugin) {
234 self.sceneWillConnectEventHandledByPlugin = YES;
237 if (!
self.sceneWillConnectEventHandledByPlugin) {
239 [
self handleDeeplinkingForEngine:engine options:connectionOptions];
243- (void)sceneDidDisconnect:(UIScene*)scene {
244 [
self updateFlutterManagedEnginesInScene:scene];
246 [engine.sceneLifeCycleDelegate sceneDidDisconnect:scene];
251#pragma mark - Transitioning to the foreground
253- (void)sceneWillEnterForeground:(UIScene*)scene {
254 [
self updateFlutterManagedEnginesInScene:scene];
258 [[
self applicationLifeCycleDelegate] sceneWillEnterForegroundFallback];
261- (void)sceneDidBecomeActive:(UIScene*)scene {
262 [
self updateFlutterManagedEnginesInScene:scene];
264 [engine.sceneLifeCycleDelegate sceneDidBecomeActive:scene];
266 [[
self applicationLifeCycleDelegate] sceneDidBecomeActiveFallback];
269#pragma mark - Transitioning to the background
271- (void)sceneWillResignActive:(UIScene*)scene {
272 [
self updateFlutterManagedEnginesInScene:scene];
274 [engine.sceneLifeCycleDelegate sceneWillResignActive:scene];
276 [[
self applicationLifeCycleDelegate] sceneWillResignActiveFallback];
279- (void)sceneDidEnterBackground:(UIScene*)scene {
280 [
self updateFlutterManagedEnginesInScene:scene];
284 [[
self applicationLifeCycleDelegate] sceneDidEnterBackgroundFallback];
287#pragma mark - Opening URLs
289- (void)scene:(UIScene*)scene openURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts {
290 [
self updateFlutterManagedEnginesInScene:scene];
293 NSMutableSet<FlutterEngine*>* enginesHandledByPlugin = [NSMutableSet set];
295 if ([
engine.sceneLifeCycleDelegate scene:scene openURLContexts:URLContexts]) {
296 [enginesHandledByPlugin addObject:engine];
301 if (enginesHandledByPlugin.count == 0) {
302 if ([[
self applicationLifeCycleDelegate] sceneFallbackOpenURLContexts:URLContexts]) {
310 if ([enginesHandledByPlugin containsObject:
engine]) {
313 for (UIOpenURLContext* urlContext in URLContexts) {
314 if ([
self handleDeeplink:urlContext.URL flutterEngine:
engine relayToSystemIfUnhandled:NO]) {
321#pragma mark - Continuing user activities
323- (void)scene:(UIScene*)scene continueUserActivity:(NSUserActivity*)userActivity {
324 [
self updateFlutterManagedEnginesInScene:scene];
327 NSMutableSet<FlutterEngine*>* enginesHandledByPlugin = [NSMutableSet set];
329 if ([
engine.sceneLifeCycleDelegate scene:scene continueUserActivity:userActivity]) {
330 [enginesHandledByPlugin addObject:engine];
335 if (enginesHandledByPlugin.count == 0) {
336 if ([[
self applicationLifeCycleDelegate] sceneFallbackContinueUserActivity:userActivity]) {
344 if ([enginesHandledByPlugin containsObject:
engine]) {
347 [
self handleDeeplink:userActivity.webpageURL flutterEngine:engine relayToSystemIfUnhandled:YES];
351#pragma mark - Saving the state of the scene
353- (NSUserActivity*)stateRestorationActivityForScene:(UIScene*)scene {
355 NSUserActivity* activity = scene.userActivity;
357 activity = [[NSUserActivity alloc] initWithActivityType:scene.session.configuration.name];
360 [
self updateFlutterManagedEnginesInScene:scene];
364 NSString* restorationId = vc.restorationIdentifier;
366 NSData* restorationData = [engine.restorationPlugin restorationData];
367 if (restorationData) {
368 [activity addUserInfoEntriesFromDictionary:@{restorationId : restorationData}];
369 [activity addUserInfoEntriesFromDictionary:@{
370 kRestorationStateAppModificationKey : [NSNumber numberWithLongLong:appBundleModifiedTime]
379- (void)scene:(UIScene*)scene
380 restoreInteractionStateWithUserActivity:(NSUserActivity*)stateRestorationActivity {
382 NSDictionary<NSString*, id>* userInfo = stateRestorationActivity.userInfo;
383 [
self updateFlutterManagedEnginesInScene:scene];
385 NSNumber* stateDateNumber = userInfo[kRestorationStateAppModificationKey];
386 int64_t stateDate = 0;
387 if (stateDateNumber && [stateDateNumber isKindOfClass:[NSNumber class]]) {
388 stateDate = [stateDateNumber longLongValue];
390 if (appBundleModifiedTime != stateDate) {
397 NSString* restorationId = vc.restorationIdentifier;
399 NSData* restorationData = userInfo[restorationId];
400 if ([restorationData isKindOfClass:[NSData class]]) {
401 [engine.restorationPlugin setRestorationData:restorationData];
407#pragma mark - Performing tasks
409- (void)windowScene:(UIWindowScene*)windowScene
410 performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
411 completionHandler:(
void (^)(
BOOL succeeded))completionHandler {
412 [
self updateFlutterManagedEnginesInScene:windowScene];
414 BOOL handledByPlugin = NO;
416 BOOL result = [engine.sceneLifeCycleDelegate windowScene:windowScene
417 performActionForShortcutItem:shortcutItem
418 completionHandler:completionHandler];
420 handledByPlugin = YES;
423 if (!handledByPlugin) {
424 [[
self applicationLifeCycleDelegate]
425 sceneFallbackPerformActionForShortcutItem:shortcutItem
426 completionHandler:completionHandler];
430#pragma mark - Helpers
433 options:(UISceneConnectionOptions*)connectionOptions {
438 for (NSUserActivity* userActivity in connectionOptions.userActivities) {
439 if ([
self handleDeeplink:userActivity.webpageURL
441 relayToSystemIfUnhandled:YES]) {
449 for (UIOpenURLContext* urlContext in connectionOptions.URLContexts) {
450 if ([
self handleDeeplink:urlContext.URL flutterEngine:
engine relayToSystemIfUnhandled:YES]) {
456- (
BOOL)handleDeeplink:(NSURL*)url
458 relayToSystemIfUnhandled:(
BOOL)throwBack {
467 [engine sendDeepLinkToFramework:url
468 completionHandler:^(BOOL success) {
469 if (!success && throwBack) {
473 completionHandler:nil];
480 if ([scene.delegate conformsToProtocol:@protocol(FlutterSceneLifeCycleProvider)]) {
481 NSObject<FlutterSceneLifeCycleProvider>* sceneProvider =
482 (NSObject<FlutterSceneLifeCycleProvider>*)scene.delegate;
483 return sceneProvider.sceneLifeCycleDelegate;
489 NSObject<FlutterSceneLifeCycleProvider>* sceneProvider =
490 (NSObject<FlutterSceneLifeCycleProvider>*)scene.delegate;
491 if ([sceneProvider respondsToSelector:@selector(sceneLifeCycleDelegate)]) {
492 id sceneLifeCycleDelegate = sceneProvider.sceneLifeCycleDelegate;
501- (void)compactNSPointerArray:(NSPointerArray*)array {
506 [array addPointer:nil];
516- (instancetype)init {
517 if (
self = [super init]) {
518 _delegates = [NSPointerArray weakObjectsPointerArray];
523- (void)addDelegate:(NSObject<FlutterSceneLifeCycleDelegate>*)delegate {
524 [_delegates addPointer:(__bridge void*)delegate];
530 [_delegates addPointer:nil];
531 [_delegates compact];