SwiftUI’s New App Lifecycle and Replacements for AppDelegate and SceneDelegate in iOS 14

During WWDC 2020, SwiftUI got its own app-lifecycle in a bid to get away from UIKit’s AppDelegate and SceneDelegate. To do so, iOS 14 now offers an App Protocol, a SceneBuilderscenePhase enumerator, and a new property wrapper UIApplicationDelegateAdaptor.

Before we look at what they are, let’s quickly brush up SceneDelegate.

SceneDelegate was introduced in iOS 13 primarily to address multi-window support on iPadOS. It brought a transition from the concept of windows to scenes and allowed us to transfer responsibilities from the AppDelegate.

Starting iOS 14, when you create a new SwiftUI Application in an Xcode 12 or above project, you’d be given an option to choose between SwiftUI App Lifecycle and UIKit App Delegate

While the latter would generate the same old AppDelegate and SceneDelegate  boilerplate code with UIHostingController used for embedding SwiftUI Views, the former would greet you with a new starting point, purely for SwiftUI.

SwiftUI App Protocol: A New Starting Point

@main
struct ProjectName: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

While the above piece of code is short, there’s a lot of automatic stuff happening under the hood. Primarily, the struct gives a birds-eye view of a SwiftUI application’s hierarchy by unifying Scenes andViews together in one place. A few important things to take note from the code are:

  • The @main attribute was introduced with Swift 5.3 and indicates that the above struct is the starting point of our SwiftUI application.

  • By conforming to the App protocol, we’re required to implement a SceneBuilder which is essentially a function builder for composing one or more scenes. Since we’re using only a single scene in the above example, we’ve implemented the computed property body. Also, the App protocol is responsible for triggering the main() method that launches a SwiftUI application.

  • WindowGroup in the above example is a container scene that wraps our SwiftUI views. Running the above application on an iPadOS or macOS can create multiple WindowGroup scenes as they support those features.

Besides WindowGroup, you can also use a DocumentGroup scene type for document-based apps or a Settings and WKNotificationScene for a macOS or watchOS.

You can also set commands modifier on the WindowGroup scene to add key shortcuts to the scene. In order to do so, we need to leverage the new CommandsBuilder to group CommandMenu that uses Commands Protocol.

Setting the computed property to @SceneBuilder is essential when you’re composing complex scenes. For instance, the one given below creates a Preference Menu scene for the macOS platform.

How to Listen to SceneDelegate Lifecycle Methods?

Despite the reduced boilerplate code, one could wonder how to listen to lifecycle updates of a scene — as we do in SceneDelegate. Gladly, we have a scenePhase enumerator that holds the current state of the scene. We can use it as an Environment property wrapper and an onChange modifier to listen to state changes of our scene as shown below:

@main
struct MyApp: App {
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { phase in
if phase == .background {
//perform cleanup
}
}
}
}

How to Use AppDelegate With SwiftUI App Protocol

The AppDelegate class forms a crucial part of our applications. Whether it’s for handling notifications or configuring Firebase, it plays a key role with its different lifecycle methods.

In order to hook the AppDelegate functionality, we’d need to bring back UIKit (in order to conform to UIApplicationDelegate) into what was a pure SwiftUI application until now. SwiftUI provides a new property wrapper UIApplicationDelegateAdaptor through which we can inject the AppDelegate instance in our SwiftUI structure:

class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
return true
}
}
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

Likewise, we can use the WKExtensionDelegateAdaptor property wrapper to provide the delegate from watchOS.

Conclusion

We saw a new property wrapper for injecting AppDelegate into our SwiftUI structure and a new function builder — SceneBuilder — for composing scenes.

The new SwiftUI App lifecycle helps kickstart SwiftUI development very quickly. You can leverage the new @SceneStorage property wrapper for state restoration across multiple scenes of your apps.

Remember SceneDelegates are neither deprecated in iOS 14 nor in the latest SwiftUI iteration. Apple has only brought a new App-Lifecycle for building native SwiftUI apps that don’t depend on UIKit.

Thanks for reading.

Looking to get started with iOS 14? I recommend Programming iOS 14: Dive Deep into Views, View Controllers, and Frameworks, Eleventh Edition (Grayscale Indian Edition)