Add iOS 12 support to a new Xcode 11 Project
Published on: November 8, 2019When 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:
- Begin by deleting the
SceneDelegate.swift
file from your project. - Next, open
AppDelegate.swift
and remove all scene related methods from that file. - Lastly, you'll need to open your
Info.plist
and remove theApplication 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!