Skip to content

[UIScene] Scene lifecycle listeners list should be associated "per UIScene" (rather than "per UISceneDelegate") #185687

Description

@hellohuanlin

Use case

For multi scene support, the current setup is:

  1. FlutterSceneDelegate contains a FlutterPluginSceneLifeCycleDelegate:

@implementation FlutterSceneDelegate
@synthesize sceneLifeCycleDelegate = _sceneLifeCycleDelegate;

  1. FlutterPluginSceneLifeCycleDelegate contains a list of engines (to pipe lifecycle events to)

@interface FlutterPluginSceneLifeCycleDelegate ()
/**
* An array of weak pointers to `FlutterEngine`s that have views within this scene. Flutter
* automatically adds engines to this array.
*
* This array is lazily cleaned up. `updateFlutterManagedEnginesInScene:` should be called before
* use to ensure it is up-to-date.
*/
@property(nonatomic, strong) NSPointerArray* flutterManagedEngines;

  1. Then engine changes scene association, we call these methods:

- (BOOL)registerSceneLifeCycleWithFlutterEngine:(FlutterEngine*)engine {

- (BOOL)unregisterSceneLifeCycleWithFlutterEngine:(FlutterEngine*)engine {

  1. Also in FlutterView, we update scene association in willMoveToWindow:

- (void)willMoveToWindow:(UIWindow*)newWindow {
// When a FlutterView moves windows, it may also be moving scenes. Add/remove the FlutterEngine
// from the FlutterSceneLifeCycleProvider.sceneLifeCycleDelegate if it changes scenes.
UIWindowScene* newScene = newWindow.windowScene;
UIWindowScene* currentScene = self.window.windowScene;
if (newScene == currentScene) {
return;
}
// Remove the engine from the previous scene if it's no longer in that window and scene.
FlutterPluginSceneLifeCycleDelegate* previousSceneLifeCycleDelegate =
[FlutterPluginSceneLifeCycleDelegate fromScene:self.previousScene];
if (previousSceneLifeCycleDelegate) {
[previousSceneLifeCycleDelegate removeFlutterManagedEngine:(FlutterEngine*)self.delegate];
self.previousScene = nil;
}
if (newScene) {
// Add the engine to the new scene's lifecycle delegate.
FlutterPluginSceneLifeCycleDelegate* newSceneLifeCycleDelegate =
[FlutterPluginSceneLifeCycleDelegate fromScene:newScene];
if (newSceneLifeCycleDelegate) {
[newSceneLifeCycleDelegate addFlutterManagedEngine:(FlutterEngine*)self.delegate];
}
} else {
// If the view is being removed from a window, store the current scene to remove the engine
// from it later when the view is added to a new window.
self.previousScene = currentScene;
}
}

  1. Then when FlutterSceneDelegate receives lifecycle calls (e.g. sceneDidEnterBackground), it forwards to all the plugins in all the associated engines.

Proposal

This setup conflated UIScene vs UISceneDelegate - When the engine moves from scene A to scene B, it's moving between UIScenes (rather than UISceneDelegates). Scene A and B may (or may not) share the same UISceneDelegate, which is just a delegate object to handle scene events.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listplatform-iosiOS applications specificallyteam-iosOwned by iOS platform teamtriaged-iosTriaged by iOS platform team

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions