---
title: Transitioning to the UIKit scene-based life cycle
framework: uikit
role: article
role_heading: Article
path: uikit/transitioning-to-the-uikit-scene-based-life-cycle
---

# Transitioning to the UIKit scene-based life cycle

Adopt the scene-based life cycle to replace the app delegate life cycle in UIKit.

## Overview

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. important: Adopting the scene-based life cycle is required. Beginning in iOS 27, iPadOS 27, Mac Catalyst 27, tvOS 27, and visionOS 27, apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch. 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> note: Supporting multiple scenes is optional, and may require restructuring your app’s data model to be scene-specific. Consider whether your app’s user experience benefits from multiple scenes before enabling it. To support multiple scenes, set UIApplicationSupportsMultipleScenes to true and give each UISceneConfiguration a unique configuration name. 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:  |   |   |   |   |  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.

## See Also

### Life cycle

- [Managing your app’s life cycle](uikit/managing-your-app-s-life-cycle.md)
- [Responding to the launch of your app](uikit/responding-to-the-launch-of-your-app.md)
- [UIApplication](uikit/uiapplication.md)
- [UIApplicationDelegate](uikit/uiapplicationdelegate.md)
- [Scenes](uikit/scenes.md)
