Weekly Swift articles, podcasts and tips by John Sundell.

Using an AppDelegate with the new SwiftUI-based app lifecycle

Published on 06 Jul 2020
Basics article available: SwiftUI

Xcode 12 introduces the option to write not just views, but entire apps using SwiftUI from top to bottom. Although it’s still likely that we’ll need to mix and match SwiftUI views with ones built using UIKit in certain places, we can now get started building a new SwiftUI-based app using a simple App struct definition:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            RootView()
        }
    }
}

For a more thorough first look at the above API and its Scene equivalent, check out this article over on WWDC by Sundell & Friends.

However, while the above App API is really neat and lightweight, it doesn’t support nearly the same amount of power and flexibility as its UIKit counterpart — UIApplicationDelegate — which was the previous go-to entry point for iOS and tvOS apps:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    ...
}

But here’s the good news: We can actually still use a good old fashioned AppDelegate even when building a SwiftUI-based app, by using the UIApplicationDelegateAdaptor property wrapper. All that we have to do is to declare such a property anywhere within our App struct, using any name we’d like, and SwiftUI will automatically detect it and use the specified type as the app’s delegate:

@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate

    var body: some Scene {
        ...
    }
}

Really cool! Worth keeping in mind, though, is that if we’re adapting an existing app delegate to be used this way, we’ll need to remove the @UIApplicationMain attribute from it (as a program can’t have two main entry points):

class AppDelegate: UIResponder, UIApplicationDelegate {
    ...
}

Finally, if all that we’re looking to use an app delegate for is to handle URLs (for example deep links), then SwiftUI now also ships with a built-in API for doing just that. To use it, we just have to apply the onOpenURL modifier to any view within our hierarchy, for example our app’s root view:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            RootView().onOpenURL { url in
                // URL handling
            }
        }
    }
}

SwiftUI’s strong interoperability with UIKit and AppKit is definitely one of its major strengths, as it enables us to adopt it in a more piecemeal fashion — and I’m glad to see that trend continue with App and UIApplicationDelegate.