Understanding the Application Lifecycle
State Changes in an iOS app
An iOS application can transition between the following states:
- Not running
- Inactive (e.g. there is a phone call)
- Active
- Background
- Suspended

The application delegate
App-level lifecycle events — launch and termination of the entire app — flow through the application delegate. On iOS 13 and later, scene-level state changes (and the window / root view controller setup) moved to the scene delegate covered below; the snippet that follows shows the pre-scene pattern for setting up the window in application(_:didFinishLaunchingWithOptions:) without a storyboard:
For example:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let rootViewController = ViewController()
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()
return true
}
Note:
UIScreen.main, used above to size the window, is deprecated as of iOS 26 — Apple recommends reading the screen from context (for exampleview.window?.windowScene?.screen) instead. It appears here because the classicAppDelegate-only lifecycle has no scene at launch; the scene-delegate setup below creates the window withUIWindow(windowScene:)and needs no screen bounds.
The scene delegate
Since iOS 13, in apps that opt into scene support, the AppDelegate is primarily responsible for the application lifecycle and setup.
The SceneDelegate is responsible for what is shown on the screen (windows or scenes) — it handles and manages the way your app is shown.
Note: Scene support is no longer just an opt-in modernization — as of the iOS 27 SDK it is required. Apple’s iOS & iPadOS 27 release notes state that apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch. Apple’s TN3187: Migrating to the UIKit scene-based life cycle walks through migrating an app that still uses the pre-scene pattern shown above (adding the
UIApplicationSceneManifestkey toInfo.plistand moving window setup into the scene delegate).
So, if you are not using storyboards you will need to set up the window and root view controller of your application in scene(_:willConnectTo:options:)
For example:
func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
let rootViewController = ViewController()
window?.rootViewController = rootViewController
window?.makeKeyAndVisible()
}
If you are using a storyboard you simply specify the “main interface” in your project settings. (Remember to also set the initial view controller on the storyboard)