How to Adopt UIScene APIs in an Existing iOS Project

Published June 17, 2020

With iOS 13, Apple introduced the idea of scenes to enable apps having and managing multiple windows. If you create a new Xcode project this new lifecycle paradigm will be set up for you, in this post I’ll walk you through the few simple steps needed to add UIScene support to an existing project.

First, add the following two UISceneSession methods to your App Delegate:

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
  // Called when a new scene session is being created.
  // Use this method to select a configuration to create 
  // the new scene with.
  return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
  // Called when the user discards a scene session.
  // If any sessions were discarded while the application 
  // was not running, this will be called shortly after
  // application:didFinishLaunchingWithOptions.
  // Use this method to release any resources that were 
  // specific to the discarded scenes, as they will not return.
}

Next, create a scene delegate that conforms to UIResponder and UIWindowSceneDelegate, like so:

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

  var window: UIWindow?

  func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the 
    // UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will 
    // automatically be initialized and attached to the scene.
    // This delegate doesn't imply the connecting scene or session are new
    // (see `application:configurationForConnectingSceneSession` instead).
    guard let _ = (scene as? UIWindowScene) else { return }
  }

  func sceneDidDisconnect(_ scene: UIScene) {}
  func sceneDidBecomeActive(_ scene: UIScene) {}
  func sceneWillResignActive(_ scene: UIScene) {}
  func sceneWillEnterForeground(_ scene: UIScene) {}
  func sceneDidEnterBackground(_ scene: UIScene) {}
}

Once your scene delegate is in place, the project’s Info.plist needs to be edited to reference it:

<key>UIApplicationSceneManifest</key>
<dict>
    <key>UIApplicationSupportsMultipleScenes</key>
    <false/>
    <key>UISceneConfigurations</key>
    <dict>
        <key>UIWindowSceneSessionRoleApplication</key>
        <array>
            <dict>
                <key>UILaunchStoryboardName</key>
                <string>LaunchScreen</string>
                <key>UISceneConfigurationName</key>
                <string>Default Configuration</string>
                <key>UISceneDelegateClassName</key>
                <string>SceneDelegate</string>
                <key>UISceneStoryboardFile</key>
                <string>Main</string>
            </dict>
        </array>
    </dict>
</dict>

And that’s it, if everything’s hooked up correctly you should now be up and running with scenes.