Transitioning to the UIKit scene-based life cycle
Adopt the scene-based life cycle to replace the app delegate life cycle in UIKit.
Overview
The UIKit scene-based life cycle separates the app process life cycle from the UI life cycle, allowing apps to manage multiple instances of UI independently. For example, a document-based app, such as a text editor, can display each open document in its own scene, letting people work on multiple documents side by side. Your app process launches once and UIApplicationDelegate handles it, but each piece of visible UI — each scene — has its own independent life cycle that UISceneDelegate and UIWindowSceneDelegate coordinate. UISceneDelegate handles basic life-cycle events that apply to all scenes, while UIWindowSceneDelegate takes care of UI-specific events like window controls and geometry changes. A scene can go to the background while another stays active, and the system can create, destroy, and arrange scenes without affecting others.
UIKit represents each scene as a UIWindowScene object. For your app’s code, this means life-cycle events no longer occur globally — they occur per scene. Tasks like saving state or updating your UI happen at the scene level rather than the app level.
For more information about configuring scene support, see Specifying the scenes your app supports.
Starting in iOS 18.4, iPadOS 18.4, Mac Catalyst 18.4, tvOS 18.4, and visionOS 2.4, UIKit logs this message for apps that haven’t migrated:
This process does not adopt UIScene lifecycle.
This will become an assert in a future version.In iOS 26, iPadOS 26, Mac Catalyst 26, tvOS 26, and visionOS 26, the message changes to:
UIScene lifecycle will soon be required.
Failure to adopt will result in an assert in the future.Determine if your app needs to migrate
Migrate to the scene-based life cycle if your app meets either of the following conditions:
The UIApplicationSceneManifest key is missing from your information property list, or it has no specified configurations.
Your app delegate doesn’t implement application(_:configurationForConnecting:options:).
Adopt the scene-based life cycle
To configure your app’s scenes, add a UIApplicationSceneManifest key with a scene configuration to your information property list. If your app requires dynamic scene configurations — such as customizing scenes based on user activities, or handling different scene roles — implement application(_:configurationForConnecting:options:) in your app delegate instead.
Configure the information property list for scene support
Add a UIApplicationSceneManifest key with a scene configuration to your information property list:
Open your Xcode project.
Select your app target.
Go to the General settings for your app target.
Select “Scene manifest” in the Deployment Info section.
Add a UIApplicationSceneManifest key to the information property list.
For example:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>Provide scene configurations from your app delegate
Implement application(_:configurationForConnecting:options:) in your app delegate if your app doesn’t include scene-configuration data in its information property list, or if it requires dynamic scene configuration — such as loading different scenes based on session-specific data:
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
// Each `UISceneConfiguration` must have a unique configuration name
// that corresponds to an entry in the information property list scene manifest.
let configurationName: String
switch options.userActivities.first?.activityType {
case UserActivity.GalleryOpenInspectorActivityType:
// Create a photo inspector window scene.
configurationName = "Inspector Configuration"
default:
// Create a default gallery window scene.
configurationName = "Default Configuration"
}
return UISceneConfiguration(
name: configurationName,
sessionRole: connectingSceneSession.role
)
}
}In this example, the app uses the activityType property to determine which scene to create. For more information about configuring your app for different scene types, see Specifying the scenes your app supports. For information about creating multiple windows programmatically, see Supporting multiple windows on iPad.
Configure your window scene
UIKit creates a UIWindowScene object for each scene instance. When configuring scene support, specify UIWindowScene objects rather than UIScene objects. If your app adopts scenes for CarPlay, use CPTemplateApplicationScene instead. To learn how to add a CarPlay scene, see Displaying Content in CarPlay.
If you load your root view controller from the storyboard, include the storyboard name in the UISceneConfigurations key in your information property list scene manifest. The system automatically configures your window scene and root view controller.
If you load your root view controller programmatically, implement scene(_:willConnectTo:options:) to create a UIWindow and associate it with the scene:
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = YourRootViewController()
window?.makeKeyAndVisible()
}
}SceneDelegate is a UIResponder subclass that conforms to UIWindowSceneDelegate. For more information about preparing your app at launch time, see Responding to the launch of your app.
Migrate app life-cycle logic
Move your app’s existing life-cycle methods from UIApplicationDelegate to UISceneDelegate:
UIApplicationDelegate | UISceneDelegate |
|---|---|
After migrating, test your app in Full Screen Apps, Windowed Apps, and Stage Manager on iPad. To learn how to respond to state transitions, see Managing your app’s life cycle.
Support noninteractive external display scenes
If your app doesn’t present noninteractive custom content on an external display, you don’t need to configure this scene role. To present noninteractive custom content on an external display, configure your app to handle the scene role that UIKit offers.
When an external display is connected, the system may offer your app a scene with the windowExternalDisplayNonInteractive role. On compatible iPad models with extended display enabled, the system presents your app in windows on the external display. On devices that don’t support extended displays, it mirrors your app’s primary display.
To suppress mirroring by presenting custom content, handle the windowExternalDisplayNonInteractive role in your scene delegate:
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
switch session.role {
case .windowApplication:
window = UIWindow(windowScene: windowScene)
window?.rootViewController = YourRootViewController()
window?.makeKeyAndVisible()
case .windowExternalDisplayNonInteractive:
// Provide a window to present noninteractive content on the external display.
// Otherwise, ignore this role to preserve default behavior.
break
default:
break
}
}Alternatively, return an empty UISceneConfiguration() for the windowExternalDisplayNonInteractive role from your app delegate. This suppresses custom content and preserves default behavior, and also prevents UIKit from instantiating a scene delegate for this role:
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
if connectingSceneSession.role == .windowExternalDisplayNonInteractive {
return UISceneConfiguration()
}
let configurationName: String
switch options.userActivities.first?.activityType {
case UserActivity.GalleryOpenInspectorActivityType:
configurationName = "Inspector Configuration"
default:
configurationName = "Default Configuration"
}
return UISceneConfiguration(
name: configurationName,
sessionRole: connectingSceneSession.role
)
}To present custom content on the external display, provide a scene configuration for the windowExternalDisplayNonInteractive role and attach a UIWindow to the scene. Content for this role spans the full screen, and attaching a window to the external display scene turns off mirroring if enabled. To restore the default behavior, set the windowScene property of the external UIWindow to nil:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var externalWindow: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
switch session.role {
case .windowApplication:
window = UIWindow(windowScene: windowScene)
window?.rootViewController = YourRootViewController()
window?.makeKeyAndVisible()
case .windowExternalDisplayNonInteractive:
externalWindow = UIWindow(windowScene: windowScene)
externalWindow?.rootViewController = YourExternalDisplayViewController()
externalWindow?.makeKeyAndVisible()
default:
break
}
}
// Stop presenting on the external display and restore the default behavior.
func exitPhotoInspector() {
externalWindow?.windowScene = nil
externalWindow = nil
}
}To provide the scene configuration, either add a windowExternalDisplayNonInteractive entry to UISceneConfiguration in your information property list, or return a UISceneConfiguration for this role from application(_:configurationForConnecting:options:).
For more information about presenting content on connected displays, see Presenting content on a connected display.