82

In Xcode 11, I created a new app project from the Single View App template. I want this app to run in iOS 12 as well as iOS 13. But when I switch the deployment target to iOS 12, I got a lot of error messages like this one:

UIWindowScene is only available in iOS 13 or newer

What should I do?

Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 6
    For anyone looking for an updated "Single View App" Xcode template that supports iOS 12 and 13 as well as supporting either a storyboard or an all-code user interface, see https://github.com/rmaddy/XcodeTemplates. – rmaddy Sep 25 '19 at 05:33

2 Answers2

108

The template in Xcode 11 uses a scene delegate. Scene delegates and the related classes are new in iOS 13; they don't exist in iOS 12 and before, and the launch process is different.

To make a project generated from an Xcode 11 app template backward compatible, you need to mark the entire SceneDelegate class, and any methods in the AppDelegate class that refer to UISceneSession, as @available(iOS 13.0, *).

You also need to declare a window property in the AppDelegate class (if you don't do that, the app will run and launch but the screen will be black):

var window : UIWindow?

The result is that when this app runs in iOS 13, the scene delegate has the window, but when it runs in iOS 12 or before, the app delegate has the window — and your other code may then need to take account of that in order to be backward compatible.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 5
    Your solution is a good one. That said, would it make sense to simply delete `SceneDelegate` if your only goal is to support iOS 12? Adding `var window : UIWindow?` back to `AppDelegate` is still necessary of course. To fix the other errors, you would also need to remove the generated "UISceneSession Lifecycle" methods from `AppDelegate` and the `UIApplicationSceneManifest` key / value from `Info.plist`. Maybe there are other reasons to keep `SceneDelegate` though? – JWK Jul 08 '19 at 06:49
  • @JWK You must use UIWindowScene going forward. That’s why it’s in the template. – matt Jul 08 '19 at 11:54
  • Oh, thanks for clarifying. Any chance you can't point me to where that's documented? I understood scenes to be opt in, and only required to display multiple copies of an app’s UI: https://developer.apple.com/documentation/uikit/app_and_scenes/specifying_the_scenes_your_app_supports. – JWK Jul 08 '19 at 13:49
  • Well there's lots of WWDC videos about it. – matt Jul 08 '19 at 13:56
  • 1
    I did see the April 2020 requirement from Session 224. I thought it pertained to making adaptive apps that support split screen multitasking as opposed to explicitly requiring multiple copy support. I may have misunderstood. – JWK Jul 08 '19 at 14:01
  • That's not what I'm talking about. I'm not saying multiple copy support is _required_. I'm saying that window scenes are the new app architecture going forward, even if there is only one scene. – matt Jul 08 '19 at 14:02
  • 2
    Sure. Just trying to confirm where specifically I can learn more about scenes being required. The April 2020 requirement came to mind from my WWDC viewing. Didn’t mean to imply that’s what you were saying :) – JWK Jul 08 '19 at 14:18
  • 12
    https://sarunw.com/tips/create-new-ios12-project-in-xcode11/ - this tutorial may help someone. – onCompletion Oct 26 '19 at 14:12
  • can you put a example for put @available(iOS 13.0, *) in UISceneSession –  Dec 03 '19 at 14:39
  • @x-rw Sketch at https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/bk2ch01p004appWithoutStoryboard2/bk2ch01p004appWithoutStoryboard – matt Dec 04 '19 at 00:46
62

Could you please add this line code like the following

STEP1:-

@available out the SceneDelegate.swift

@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

//...

}

STEP2:-

@available out some methods in AppDelegate.swift

// AppDelegate.swift

@available(iOS 13.0, *)
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)
}

@available(iOS 13.0, *)
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.
}

STEP3:-

You should declare window property in AppDelegate.swift file like var window: UIWindow?

class AppDelegate: UIResponder, UIApplicationDelegate {

     var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }
IKKA
  • 6,297
  • 7
  • 50
  • 88
  • but how this will work with programmatic implementation? (without Storyboard) – Hattori Hanzō Mar 13 '20 at 11:03
  • 1
    @HattoriHanzō without storyboard : We can declare in AppDelegate var window: UIWindow? var rootViewController:ViewController! And In didFinishLaunching method add these lines : rootViewController = ViewController() self.window = UIWindow.init() self.window?.bounds = UIScreen.main.bounds self.window?.rootViewController = rootViewController self.window?.backgroundColor = .white self.window?.makeKeyAndVisible() – Dilip Saket Apr 04 '20 at 17:40