Add iOS 12 support to a new Xcode 11 Project

Published on: November 8, 2019

When you create a new project in Xcode 11, you automatically get the new SceneDelegate for free. This is great if you want to build an app that's for iOS 13 and newer but as soon as you change your deployment target to an iOS version that's lower than iOS 13, your app will have trouble compiling. In this Quick Tip, I will show you how to update your project in order to make it compile for iOS 12 and below. You will first learn how to use the SceneDelegate for iOS 13 and up, and use the AppDelegate as a fallback for older versions of iOS. After that, I will show you how to opt-out of using the SceneDelegate completely if you're absolutely sure that you're not interested in any of its benefits for iOS 13.

Making the existing template work for iOS 12

Since the UIWindowSceneDelegate is only available in iOS 13 and up, we'll need to exclude the entire SceneDelegate object if the app is compiled for iOS 12 or below. To do this, add an @available annotation to the SceneDelegate class as shown in the following code snippet:

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

This ensures that the SceneDelegate is not included in iOS 12 builds. Next, go to the AppDelegate and add the same @available annotation to application(_:configurationForConnecting:options) and application(_:didDiscardSceneSessions) as shown in the following code snippet:

@available(iOS 13, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
  // ...

@available(iOS 13, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
  // ...

Lastly, since applications that use storyboards must a window property defined on the AppDelegate for iOS 12 and below, add the following property to the AppDelegate:

var window: UIWindow?

This will allow iOS to instantiate the Main.storyboard and use it to create and populate the window object on iOS 12. For iOS 13 and above the window will remain unset because the SceneDelegate will set its own window on those versions of iOS.

Your app should be able to run on iOS 12 and 13 now. One last thing you might want to add to application(_:didFinishLaunchingWithOptions:) is the following:

if #available(iOS 13, *) {
  // do nothing / do things that should only be done for iOS 13
} else {
  // do iOS 12 specific window setup

This will allow you to do some version dependent set up in your AppDelegate.

Keep in mind that now that you have the AppDelegate for iOS 12 and the SceneDelegate for iOS 13 you have different entry points in your code for different iOS versions. This means that you might have to do some special setup or apply duplicated logic in the AppDelegate or SceneDelegate depending on what you need to achieve. If you don't want to do this, you can also opt-out of the SceneDelegate completely. Let's see how.

Opting out of the SceneDelegate completely

To opt-out of using the SceneDelegate in your project, you should take the following steps:

  1. Begin by deleting the SceneDelegate.swift file from your project.
  2. Next, open AppDelegate.swift and remove all scene related methods from that file.
  3. Lastly, you'll need to open your Info.plist and remove the Application Scene Manifest key from that file.

Three simple steps and you're able to work on your project in the same way you used to do in Xcode 10. Very nice.

In summary

In this Quick Tip, you've learned how to add a couple of clever @available and if #available statements to your code to make sure your projects work on iOS 12 and below. While this isn't a lot of work, I think it's still somewhat inconvenient that we have to do this to make Xcode 11 work for in a very reasonable scenario. Supporting older iOS versions is something a lot of developers have to do so it would have been nice to see Apple accommodate this when creating a new project in Xcode. I would recommend against using the approach of opting out of the SceneDelegate entirely because it has some awesome advantages that I describe in my posts Understanding the iOS 13 Scene Delegate and Adding support for multiple windows to your iPadOS app.

When you deploy your app to iOS 12 and use the SceneDelegate for iOS 13, you will run into some come duplication between the AppDelegate for iOS 12 and the SceneDelegate for iOS 13. I chose to omit suggestions to make this manageable because I really wanted to keep this post short. I'm sure you can come up with some way to encapsulate the duplicated logic in some kind of helper object that you can use to configure your app in either the AppDelegate or the SceneDelegate. I might write something about this in the future. Make sure to follow me on Twitter. Don't hesitate to reach out to me if you have any questions or suggestions for me!


Quick Tip

Subscribe to my newsletter