diff --git a/sites/docs/src/content/release/breaking-changes/uiscenedelegate.md b/sites/docs/src/content/release/breaking-changes/uiscenedelegate.md
index 39e5f629b8..b15003cd9d 100644
--- a/sites/docs/src/content/release/breaking-changes/uiscenedelegate.md
+++ b/sites/docs/src/content/release/breaking-changes/uiscenedelegate.md
@@ -1,8 +1,8 @@
---
-title: UISceneDelegate adoption
+title: UIScene adoption
description: >-
Learn how to migrate your Flutter iOS app, add-to-app integration, or plugin
- to Apple's required UIScene lifecycle using FlutterSceneDelegate.
+ to Apple's required UIScene lifecycle.
---
{% render "docs/breaking-changes.md" %}
@@ -595,143 +595,8 @@ forward scene lifecycle events to Flutter.
### If your app supports multiple scenes
-When multiple scenes are enabled (`UIApplicationSupportsMultipleScenes`),
-Flutter can't automatically connect a `FlutterEngine` to its
-corresponding `UIScene` during the initial scene connection phase.
-
-To ensure that Flutter plugins can receive the initial scene setup options
-(such as deep link URLs or shortcut items passed inside the
-`UIScene.ConnectionOptions` payload), you must manually register the
-`FlutterEngine` with either your `FlutterSceneDelegate` or your
-`FlutterPluginSceneLifeCycleDelegate` inside the
-`scene:willConnectToSession:options:` method.
-
-If you don't perform this manual registration,
-the `FlutterEngine` still registers itself automatically once the view
-created by the `FlutterViewController` is added to the active view hierarchy.
-However, by that point, the engine and its plugins have already missed
-any launch connection events passed during `willConnectToSession:`.
-
-
-
-
-```swift title="SceneDelegate.swift"
-import Flutter
-import FlutterPluginRegistrant
-import UIKit
-
-class SceneDelegate: FlutterSceneDelegate {
- let flutterEngine = FlutterEngine(name: "my flutter engine")
-
- override func scene(
- _ scene: UIScene,
- willConnectTo session: UISceneSession,
- options connectionOptions: UIScene.ConnectionOptions
- ) {
- guard let windowScene = scene as? UIWindowScene else { return }
- window = UIWindow(windowScene: windowScene)
-
- flutterEngine.run()
- GeneratedPluginRegistrant.register(with: flutterEngine)
-
- // If using FlutterSceneDelegate:
- self.registerSceneLifeCycle(with: flutterEngine)
-
- // If using FlutterSceneLifeCycleProvider:
- // sceneLifeCycleDelegate.registerSceneLifeCycle(with: flutterEngine)
-
- let viewController = ViewController(engine: flutterEngine)
- window?.rootViewController = viewController
- window?.makeKeyAndVisible()
- super.scene(scene, willConnectTo: session, options: connectionOptions)
- }
-}
-```
-
-
-
-
-```objc title="SceneDelegate.h"
-#import
-#import
-#import
-
-@interface SceneDelegate : FlutterSceneDelegate
-@property(nonatomic, strong) FlutterEngine *flutterEngine;
-@end
-```
-
-```objc title="SceneDelegate.m"
-#import "SceneDelegate.h"
-#import "ViewController.h"
-
-@implementation SceneDelegate
-
-- (instancetype)init {
- if (self = [super init]) {
- _flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];
- }
- return self;
-}
-
-- (void)scene:(UIScene *)scene
- willConnectToSession:(UISceneSession *)session
- options:(UISceneConnectionOptions *)connectionOptions {
- if (![scene isKindOfClass:[UIWindowScene class]]) {
- return;
- }
- UIWindowScene *windowScene = (UIWindowScene *)scene;
- self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
-
- [self.flutterEngine run];
- [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
-
- // If using FlutterSceneDelegate:
- [self registerSceneLifeCycleWithFlutterEngine:self.flutterEngine];
-
- // If using FlutterSceneLifeCycleProvider:
- // [self.sceneLifeCycleDelegate registerSceneLifeCycleWithFlutterEngine:self.flutterEngine];
-
- ViewController *viewController = [[ViewController alloc] initWithEngine:self.flutterEngine];
- self.window.rootViewController = viewController;
- [self.window makeKeyAndVisible];
- [super scene:scene willConnectToSession:session options:connectionOptions];
-}
-
-@end
-```
-
-
-
-
-If you manually register a `FlutterEngine` with a scene,
-you must also unregister it if the view
-created by the `FlutterEngine` changes scenes.
-
-
-
-
-```swift
-// If using FlutterSceneDelegate:
-self.unregisterSceneLifeCycle(with: flutterEngine)
-
-// If using FlutterSceneLifeCycleProvider:
-sceneLifeCycleDelegate.unregisterSceneLifeCycle(with: flutterEngine)
-```
-
-
-
-
-```objc
-// If using FlutterSceneDelegate:
-[self unregisterSceneLifeCycleWithFlutterEngine:self.flutterEngine];
-
-// If using FlutterSceneLifeCycleProvider:
-[self.sceneLifeCycleDelegate unregisterSceneLifeCycleWithFlutterEngine:self.flutterEngine];
-```
-
-
-
+Flutter doesn't fully support multiple scenes yet,
+but support is in progress.
@@ -821,7 +686,6 @@ migrate it to UIKit's scene-based lifecycle as follows:
| [`application:continueUserActivity:restorationHandler:`][] | [`scene:continueUserActivity:`][] |
| [`application:performActionForShortcutItem:completionHandler:`][] | [`windowScene:performActionForShortcutItem:completionHandler:`][] |
| [`application:openURL:options:`][] | [`scene:openURLContexts:`][] |
- | [`application:performFetchWithCompletionHandler:`][] | [`BGAppRefreshTask`][] |
| [`application:willFinishLaunchingWithOptions:`][] | [`scene:willConnectToSession:options:`][] |
| [`application:didFinishLaunchingWithOptions:`][] | [`scene:willConnectToSession:options:`][] |
@@ -911,6 +775,120 @@ migrate it to UIKit's scene-based lifecycle as follows:
Move any logic that relies on the launch options to the
`scene:willConnectToSession:options:` event.
+ For apps that adopt `UIScene`, Flutter calls
+ `application:willFinishLaunchingWithOptions:` and
+ `application:didFinishLaunchingWithOptions:` during the
+ `scene:willConnectToSession:options:` callback.
+ However, some Apple APIs must be called before
+ `application:didFinishLaunchingWithOptions:` returns.
+ To work around this restriction, plugins can expose an API that
+ lets you register background tasks directly in the `AppDelegate`'s
+ `application:willFinishLaunchingWithOptions:` method.
+
+ The following Apple APIs must be configured before app launch finishes:
+
+ - [`BGTaskScheduler.registerForTaskWithIdentifier:usingQueue:launchHandler:`][]
+
+ > Registration of all launch handlers must be complete
+ > before the end of `applicationDidFinishLaunching:`.
+
+ - [`UNUserNotificationCenterDelegate`][]
+
+ > You must assign your delegate object to the `UNUserNotificationCenter`
+ > object before your app finishes launching.
+ > For example, in an iOS app, you must assign it in the
+ > `application:willFinishLaunchingWithOptions:` or
+ > `application:didFinishLaunchingWithOptions:`
+ > method of your app delegate.
+ > Assigning a delegate after the system calls these methods
+ > might cause you to miss incoming notifications.
+
+ - [`HKHealthStore.enableBackgroundDeliveryForType:frequency:withCompletion:`][]
+
+ > If you plan on supporting background delivery,
+ > set up all your observer queries in your app delegate’s
+ > `application:didFinishLaunchingWithOptions:` method.
+ > By setting up the queries in
+ > `application:didFinishLaunchingWithOptions:`,
+ > you ensure that you’ve instantiated your queries,
+ > and they’re ready to use before HealthKit delivers the updates.
+
+ For example, to support `BGTaskScheduler`:
+
+
+
+ ```swift diff
+ public class BGTaskPlugin: NSObject, FlutterPlugin {
+ public static let shared = BGTaskPlugin()
+ public static func register(with registrar: FlutterPluginRegistrar) {
+ let channel = ...
+ registrar.addMethodCallDelegate(shared, channel: channel)
+ registrar.addApplicationDelegate(shared)
+ }
+ + // A new API for apps to call in their `AppDelegate`'s `application:didFinishLaunchingWithOptions:`
+ + public func registerBackgroundHandler(identifer: String) {
+ + BGTaskScheduler.register(forTaskWithIdentifier: identifier, using: .main) { task in
+ + // Handle background task execution
+ + }
+ + }
+ - public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [AnyHashable : Any] = [:]) -> Bool {
+ - BGTaskScheduler.register(forTaskWithIdentifier: identifier, using: .main) { task in
+ - // Handle background task execution
+ - }
+ - return true
+ - }
+ }
+ ```
+
+
+
+
+ ```objc diff
+ @interface BGTaskPlugin : NSObject
+ + + (instancetype)sharedInstance;
+ + - (void)registerBackgroundHandlerWithIdentifier:(NSString *)identifier;
+ @end
+
+ @implementation BGTaskPlugin
+
+ + + (instancetype)sharedInstance {
+ + static BGTaskPlugin *sharedInstance = nil;
+ + static dispatch_once_t onceToken;
+ + dispatch_once(&onceToken, ^{
+ + sharedInstance = [[BGTaskPlugin alloc] init];
+ + });
+ + return sharedInstance;
+ + }
+
+ + (void)registerWithRegistrar:(NSObject *)registrar {
+ FlutterMethodChannel *channel = ...
+ [registrar addMethodCallDelegate:[BGTaskPlugin sharedInstance] channel:channel];
+ [registrar addApplicationDelegate:[BGTaskPlugin sharedInstance]];
+ }
+
+ + // A new API for apps to call in their `AppDelegate`'s `application:didFinishLaunchingWithOptions:`
+ + - (void)registerBackgroundHandlerWithIdentifier:(NSString *)identifier {
+ + [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:identifier
+ + usingQueue:dispatch_get_main_queue()
+ + launchHandler:^(BGTask *task) {
+ + // Handle background task execution
+ + }];
+ + }
+
+ - - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ - [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:identifier
+ - usingQueue:dispatch_get_main_queue()
+ - launchHandler:^(BGTask *task) {
+ - // Handle background task execution
+ - }];
+ - return YES;
+ - }
+ @end
+ ```
+
+
+
+
1. Optional: Migrate other deprecated APIs to support
multiple scenes in the future.
@@ -1022,7 +1000,6 @@ migrate it to UIKit's scene-based lifecycle as follows:
[`application:continueUserActivity:restorationHandler:`]: {{site.apple-dev}}/documentation/uikit/uiapplicationdelegate/1623072-application
[`application:performActionForShortcutItem:completionHandler:`]: {{site.apple-dev}}/documentation/uikit/uiapplicationdelegate/application(_:performactionfor:completionhandler:)
[`application:openURL:options:`]: {{site.apple-dev}}/documentation/uikit/uiapplicationdelegate/1623112-application
-[`application:performFetchWithCompletionHandler:`]: {{site.apple-dev}}/documentation/uikit/uiapplicationdelegate/1623125-application
[`application:willFinishLaunchingWithOptions:`]: {{site.apple-dev}}/documentation/uikit/uiapplicationdelegate/1623032-application
[`application:didFinishLaunchingWithOptions:`]: {{site.apple-dev}}/documentation/uikit/uiapplicationdelegate/1622921-application
[`sceneDidBecomeActive`]: {{site.apple-dev}}/documentation/uikit/uiscenedelegate/3197915-scenedidbecomeactive
@@ -1032,7 +1009,6 @@ migrate it to UIKit's scene-based lifecycle as follows:
[`scene:continueUserActivity:`]: {{site.apple-dev}}/documentation/uikit/uiscenedelegate/scene(_:continue:)
[`windowScene:performActionForShortcutItem:completionHandler:`]: {{site.apple-dev}}/documentation/uikit/uiwindowscenedelegate/3238088-windowscene
[`scene:openURLContexts:`]: {{site.apple-dev}}/documentation/uikit/uiscenedelegate/3238059-scene
-[`BGAppRefreshTask`]: {{site.apple-dev}}/documentation/backgroundtasks/bgapprefreshtask
[`scene:willConnectToSession:options:`]: {{site.apple-dev}}/documentation/uikit/uiscenedelegate/3197914-scene
[`UIScreen mainScreen`]: {{site.apple-dev}}/documentation/uikit/uiscreen/1617815-mainscreen
[`UIWindowScene screen`]: {{site.apple-dev}}/documentation/uikit/uiwindowscene/screen?language=objc
@@ -1042,6 +1018,9 @@ migrate it to UIKit's scene-based lifecycle as follows:
[`UIWindowScene windows`]: {{site.apple-dev}}/documentation/uikit/uiwindowscene/windows?language=objc
[`UIApplicationDelegate window`]: {{site.apple-dev}}/documentation/uikit/uiapplicationdelegate/window
[`UIView window`]: {{site.apple-dev}}/documentation/uikit/uiview/window?language=objc
+[`BGTaskScheduler.registerForTaskWithIdentifier:usingQueue:launchHandler:`]: {{site.apple-dev}}/documentation/backgroundtasks/bgtaskscheduler/register(fortaskwithidentifier:using:launchhandler:)?language=objc
+[`UNUserNotificationCenterDelegate`]: {{site.apple-dev}}/documentation/usernotifications/unusernotificationcenterdelegate?language=objc
+[`HKHealthStore.enableBackgroundDeliveryForType:frequency:withCompletion:`]: {{site.apple-dev}}/documentation/healthkit/hkhealthstore/enablebackgrounddelivery(for:frequency:withcompletion:)?language=objc
## Bespoke FlutterViewController usage