Skip to content

Migrating AppDelegate to SceneDelegate

Aug 3, 2025

This document provides an in-depth exploration of AppDelegate and SceneDelegate in iOS applications, their respective functions, a comparison table mapping AppDelegate functions to their SceneDelegate equivalents, and a detailed guide on migrating from AppDelegate to SceneDelegate for apps adopting the Scene-based architecture introduced in iOS 13.

1. AppDelegate

Overview

The AppDelegate class is a core component of an iOS application, serving as the entry point for the app and managing its lifecycle. It is responsible for handling app-level events, such as launching, terminating, and responding to system notifications. The AppDelegate is typically implemented in a class conforming to the UIApplicationDelegate protocol.

Key Functions of AppDelegate

The AppDelegate handles application-wide events and configurations. Below are the primary methods defined in the UIApplicationDelegate protocol, organized by their purpose:

Application Lifecycle Methods

  • application(_:didFinishLaunchingWithOptions:)

    • Purpose: Called when the app has finished launching.
    • Usage: Initialize app-wide resources, configure the initial UI, and perform setup tasks (e.g., setting up third-party libraries, restoring app state, or configuring the root view controller).
    • Example:
      swift
      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
          window = UIWindow(frame: UIScreen.main.bounds)
          window?.rootViewController = UINavigationController(rootViewController: ViewController())
          window?.makeKeyAndVisible()
          return true
      }
    • Notes: This is often the first method called after the app starts. Return true to indicate successful initialization.
  • applicationDidBecomeActive(_:)

    • Purpose: Called when the app becomes the active app (e.g., after launching or resuming from the background).
    • Usage: Refresh UI, resume tasks, or start animations.
    • Notes: This method is called after the app transitions to the foreground and is fully active.
  • applicationWillResignActive(_:)

    • Purpose: Called when the app is about to move from the active to inactive state (e.g., due to an incoming call or user switching apps).
    • Usage: Pause ongoing tasks, disable timers, or save temporary state.
    • Notes: Avoid heavy processing, as the app may transition quickly to the background.
  • applicationDidEnterBackground(_:)

    • Purpose: Called when the app enters the background.
    • Usage: Save user data, release shared resources, and store app state for restoration.
    • Notes: The app has limited time to execute tasks in this method, so use background tasks if needed.
  • applicationWillEnterForeground(_:)

    • Purpose: Called when the app is about to enter the foreground from the background.
    • Usage: Undo changes made in applicationDidEnterBackground(_:), such as restarting paused tasks.
    • Notes: Prepare the app to resume active operation.
  • applicationWillTerminate(_:)

    • Purpose: Called when the app is about to terminate.
    • Usage: Perform final cleanup, save critical data, and release resources.
    • Notes: Termination may not always occur (e.g., if the app is suspended), so save state proactively.

Other Important Methods

  • application(_:open:options:)

    • Purpose: Handles opening URLs (e.g., deep links or universal links).
    • Usage: Process incoming URLs to navigate to specific content or handle custom URL schemes.
  • application(_:didReceiveRemoteNotification:fetchCompletionHandler:)

    • Purpose: Handles remote push notifications.
    • Usage: Process notification data and update the app’s state.
  • application(_:configurationForConnecting:options:)

    • Purpose: Introduced in iOS 13 to support SceneDelegate; provides a scene configuration for a new scene session.
    • Usage: Specify which scene configuration to use for a new session.
  • application(_:didDiscardSceneSessions:)

    • Purpose: Called when the user discards scene sessions (e.g., closing app windows in a multi-window app).
    • Usage: Clean up resources associated with discarded scenes.

Role in Pre-iOS 13 Apps

Before iOS 13, AppDelegate was responsible for managing both the app’s lifecycle and the UI lifecycle (e.g., setting up the UIWindow and root view controller). It acted as the central coordinator for the entire application, handling all lifecycle events for the app and its single window.

2. SceneDelegate

Overview

Introduced in iOS 13, the SceneDelegate class is part of the Scene-based architecture, which allows iOS apps to support multiple windows (scenes) on devices like iPads and iPhones with iOS 13 or later. The SceneDelegate manages the lifecycle of individual scenes (UI instances) within the app, offloading UI-related responsibilities from the AppDelegate.

Key Functions of SceneDelegate

The SceneDelegate conforms to the UISceneDelegate protocol and handles scene-specific lifecycle events. Below are the primary methods:

Scene Lifecycle Methods

  • scene(_:willConnectTo:options:)

    • Purpose: Called when a new scene is created and connected to the app.
    • Usage: Set up the scene’s UIWindow, configure the root view controller, and initialize scene-specific resources.
    • Example:
      swift
      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 = UINavigationController(rootViewController: ViewController())
          window?.makeKeyAndVisible()
      }
    • Notes: This is the SceneDelegate equivalent of application(_:didFinishLaunchingWithOptions:) for UI setup.
  • sceneDidDisconnect(_:)

    • Purpose: Called when a scene is disconnected (e.g., when the user closes a window or the system terminates it).
    • Usage: Clean up scene-specific resources and save state.
    • Notes: The scene session may be discarded or reused later.
  • sceneDidBecomeActive(_:)

    • Purpose: Called when the scene becomes active (e.g., when the user interacts with the window).
    • Usage: Resume scene-specific tasks, refresh UI, or start animations.
  • sceneWillResignActive(_:)

    • Purpose: Called when the scene is about to become inactive.
    • Usage: Pause scene-specific tasks or save temporary state.
  • sceneWillEnterForeground(_:)

    • Purpose: Called when the scene is about to enter the foreground.
    • Usage: Prepare the scene to become active again, such as restoring UI state.
  • sceneDidEnterBackground(_:)

    • Purpose: Called when the scene enters the background.
    • Usage: Save scene-specific data and release resources.

Other Important Methods

  • scene(_:openURLContexts:)

    • Purpose: Handles URL opening for a specific scene.
    • Usage: Process deep links or universal links for the scene’s context.
  • scene(_:continue:)

    • Purpose: Handles continuing a user activity (e.g., Handoff or Spotlight actions).
    • Usage: Restore the scene to a specific state based on the activity.

Role in iOS 13 and Later

The SceneDelegate manages the lifecycle of individual scenes, which represent separate instances of the app’s UI. This enables features like multi-window support on iPadOS and improved state restoration. Each scene has its own UIWindow and lifecycle, independent of other scenes in the same app.

3. Comparison of AppDelegate and SceneDelegate Functions

The following table maps AppDelegate methods to their corresponding SceneDelegate methods, highlighting their roles and differences:

AppDelegate MethodSceneDelegate MethodPurposeNotes
application(_:didFinishLaunchingWithOptions:)scene(_:willConnectTo:options:)Initialize the app or scene, set up the UI (e.g., UIWindow, root view controller).In AppDelegate, this handles both app and UI setup (pre-iOS 13). In SceneDelegate, it focuses on scene-specific UI setup.
applicationDidBecomeActive(_:)sceneDidBecomeActive(_:)Handle the app/scene becoming active.AppDelegate manages app-wide activation; SceneDelegate manages scene-specific activation (e.g., a specific window).
applicationWillResignActive(_:)sceneWillResignActive(_:)Handle the app/scene transitioning to an inactive state.Similar roles, but SceneDelegate applies to individual scenes, allowing per-window state management.
applicationDidEnterBackground(_:)sceneDidEnterBackground(_:)Handle the app/scene entering the background.AppDelegate saves app-wide state; SceneDelegate saves scene-specific state.
applicationWillEnterForeground(_:)sceneWillEnterForeground(_:)Prepare the app/scene to enter the foreground.SceneDelegate allows restoring UI state for individual scenes.
applicationWillTerminate(_:)sceneDidDisconnect(_:)Clean up before app/scene termination.applicationWillTerminate(_:) is app-wide and called only when the app fully terminates; sceneDidDisconnect(_:) is called when a scene is closed (e.g., in multi-window apps).
application(_:open:options:)scene(_:openURLContexts:)Handle URL opening (e.g., deep links).AppDelegate handles app-wide URL processing; SceneDelegate handles URLs for a specific scene.
application(_:didReceiveRemoteNotification:fetchCompletionHandler:)No direct equivalentHandle push notifications.Push notifications remain app-wide, typically handled in AppDelegate. Scene-specific notifications may be processed in SceneDelegate if relevant to a specific window.
application(_:continue:restoreHandler:)scene(_:continue:)Handle user activities (e.g., Handoff, Spotlight).AppDelegate handles app-wide activities; SceneDelegate restores state for a specific scene.
application(_:configurationForConnecting:options:)N/AProvide scene configuration for new sessions.Exclusive to AppDelegate in iOS 13+ to support SceneDelegate.
application(_:didDiscardSceneSessions:)N/AClean up discarded scene sessions.Exclusive to AppDelegate for managing scene session lifecycle.

Key Observations

  • UI Management: In pre-iOS 13 apps, AppDelegate handles both app and UI lifecycle events. In iOS 13+, UI-related tasks (e.g., UIWindow setup) move to SceneDelegate.
  • Granularity: SceneDelegate methods are scene-specific, enabling multi-window support, while AppDelegate methods are app-wide.
  • New Responsibilities: AppDelegate gains scene-related methods (configurationForConnecting, didDiscardSceneSessions) to support the Scene-based architecture.

4. Migrating from AppDelegate to SceneDelegate

Why Migrate?

Starting with iOS 13, Apple introduced the Scene-based architecture to support multiple windows and improve app lifecycle management. Migrating to SceneDelegate is necessary to take advantage of modern iOS features, such as:

  • Multi-window support on iPadOS.
  • Improved state restoration for individual windows.
  • Better separation of concerns between app-wide and UI-specific logic.

Migration Steps

Migrating an app from using only AppDelegate to supporting SceneDelegate involves restructuring the app to handle scene-based lifecycle events. Below is a step-by-step guide:

Step 1: Update the Project to Support Scenes

  1. Check Deployment Target:

    • Ensure the app’s deployment target is iOS 13.0 or later, as SceneDelegate is not available in earlier versions.
    • If the app must support iOS 12 or earlier, implement conditional logic to handle both AppDelegate and SceneDelegate.
  2. Add Scene Support to Info.plist:

    • Open the app’s Info.plist file and add the UIApplicationSceneManifest key to enable scene support.
    • Example configuration:
      xml
      <key>UIApplicationSceneManifest</key>
      <dict>
          <key>UIApplicationSupportsMultipleScenes</key>
          <true/>
          <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>
    • Explanation:
      • UIApplicationSupportsMultipleScenes: Set to true to enable multi-window support (optional).
      • UISceneConfigurationName: A unique name for the scene configuration.
      • UISceneDelegateClassName: Specifies the SceneDelegate class.
      • UISceneStoryboardFile: (Optional) Specifies the storyboard for the scene.
  3. Remove UIWindow from AppDelegate:

    • If your AppDelegate creates a UIWindow, remove this code, as the SceneDelegate will now manage the window.

Step 2: Create a SceneDelegate Class

  1. Add a SceneDelegate File:

    • In Xcode, create a new Swift file named SceneDelegate.swift.
    • Implement a class that conforms to UISceneDelegate:
      swift
      import UIKit
      
      class SceneDelegate: UIResponder, UISceneDelegate {
          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 = UINavigationController(rootViewController: ViewController())
              window?.makeKeyAndVisible()
          }
      
          func sceneDidDisconnect(_ scene: UIScene) { }
          func sceneDidBecomeActive(_ scene: UIScene) { }
          func sceneWillResignActive(_ scene: UIScene) { }
          func sceneWillEnterForeground(_ scene: UIScene) { }
          func sceneDidEnterBackground(_: UIScene) { }
      }
    • Notes: Move the UI setup code (e.g., creating the UIWindow and setting the root view controller) from application(_:didFinishLaunchingWithOptions:) in AppDelegate to scene(_:willConnectTo:options:) in SceneDelegate.
  2. Update AppDelegate:

    • Modify AppDelegate to support scene-based lifecycle by implementing scene configuration methods:
      swift
      import UIKit
      
      @UIApplicationMain
      class AppDelegate: UIResponder, UIApplicationDelegate {
          func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
              // Perform app-wide initialization (e.g., configure third-party libraries)
              return true
          }
      
          func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
              let configuration = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
              configuration.delegateClass = SceneDelegate.self
              return configuration
          }
      
          func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
              // Clean up discarded scenes
          }
      }
    • Notes: The configurationForConnecting method specifies the scene configuration and delegate class.

Step 3: Refactor Lifecycle Management

  1. Move UI-Related Code:

    • Relocate UI-related code (e.g., window setup, root view controller configuration) from AppDelegate to SceneDelegate.
    • Example: Move window setup from application(_:didFinishLaunchingWithOptions:) to scene(_:willConnectTo:options:).
  2. Handle Scene-Specific Lifecycle:

    • Implement scene lifecycle methods in SceneDelegate to manage UI state (e.g., saving/restoring UI state, refreshing views).
    • Example: Save scene-specific state in sceneDidEnterBackground(_:) and restore it in sceneWillEnterForeground(_:).
  3. Support Multiple Scenes:

    • If enabling multi-window support, ensure each scene has its own UIWindow and state management.
    • Handle scene creation and destruction appropriately in SceneDelegate methods.

Step 4: Support Older iOS Versions

If the app supports iOS 12 or earlier, implement conditional logic to handle both AppDelegate and SceneDelegate:

swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        if #available(iOS 13.0, *) {
            // SceneDelegate handles UI setup
            return true
        } else {
            // Fallback for iOS 12 and earlier
            window = UIWindow(frame: UIScreen.main.bounds)
            window?.rootViewController = UINavigationController(rootViewController: ViewController())
            window?.makeKeyAndVisible()
            return true
        }
    }
}
  • Notes: Use the #available check to ensure compatibility with older iOS versions.

Step 5: Test the Migration

  1. Test Lifecycle Events:

    • Verify that SceneDelegate methods are called correctly during scene lifecycle events (e.g., connecting, disconnecting, foreground/background transitions).
    • Ensure AppDelegate handles app-wide events (e.g., push notifications, URL handling).
  2. Test Multi-Window Support:

    • On iPadOS, test opening multiple windows to ensure each scene is managed correctly.
    • Verify state restoration for each scene.
  3. Test Backward Compatibility:

    • If supporting iOS 12 or earlier, test the app on older devices/simulators to ensure AppDelegate fallback works.

Step 6: Handle Advanced Features

  • State Restoration:

    • Use NSUserActivity and scene restoration APIs to save and restore scene state.
    • Implement scene(_:continue:) and scene(_:willConnectTo:options:) to handle state restoration.
  • URL Handling:

    • Move URL handling from application(_:open:options:) in AppDelegate to scene(_:openURLContexts:) in SceneDelegate for scene-specific URLs.
  • Push Notifications:

    • Handle scene-specific notifications in SceneDelegate if they affect a particular window’s content.

Best Practices

  • Separation of Concerns: Keep AppDelegate for app-wide tasks (e.g., configuring analytics, push notifications) and SceneDelegate for UI-related tasks (e.g., window management, view controller setup).
  • State Management: Ensure each scene maintains its own state, especially for multi-window apps.
  • Modular Code: Refactor shared logic (e.g., view controller setup) into reusable functions to avoid duplication between AppDelegate and SceneDelegate.
  • Testing: Thoroughly test lifecycle transitions, especially for multi-window scenarios and state restoration.

Common Challenges and Solutions

  • Challenge: App crashes on iOS 13+ due to missing SceneDelegate.
    • Solution: Ensure Info.plist includes UIApplicationSceneManifest and a SceneDelegate class is implemented.
  • Challenge: UI not appearing after migration.
    • Solution: Verify that scene(_:willConnectTo:options:) sets up the UIWindow and root view controller correctly.
  • Challenge: State restoration not working.
    • Solution: Implement state restoration APIs and test with NSUserActivity to ensure scene state is saved and restored.

Conclusion

The transition from AppDelegate to SceneDelegate reflects Apple’s shift toward a more modular and flexible app architecture. By offloading UI management to SceneDelegate, apps can support modern iOS features like multi-window interfaces while keeping AppDelegate focused on app-wide tasks. The comparison table above clarifies the relationship between AppDelegate and SceneDelegate methods, aiding in a smoother migration. With careful refactoring and testing, migrating to SceneDelegate enables a more robust and future-proof app structure.

Released under the MIT License.