iOS

SwiftUI App Lifecycle: Where to Put AppDelegate Code?


Introduction

With iOS 13 supporting SwiftUI, iOS 14 has introduced an app lifecycle. Developers today are trying to rethink their app design from the erstwhile AppDelegate and SceneDelegate methods to the declarative world of SwiftUI. While this declarative way of life makes app-building easier, it poses a common dilemma: Where does all that old AppDelegate stuff go in a SwiftUI app?

This article highlights the fundamental differences between traditional and SwiftUI lifecycles and talks about the transition of AppDelegate logic into a SwiftUI project.

Explaining the SwiftUI App Lifecycle

The entry point for your app is always the @main struct which conforms to App protocol in SwiftUI app lifecycle. Here's an example:

import SwiftUI@mainstruct MyApp: App { var body: some Scene { WindowGroup { ContentView() } }}

 

The App protocol does not require that you use an AppDelegate or SceneDelegate given that a declarative structure is laid out for the entry point into your app. Still, some use cases require AppDelegate functionalities like:

  • Handle push notifications
  • Maintain background tasks
  • Launch customization of app behavior
  • Integrate third-party libraries (for example, Firebase, Facebook SDK).

Methods to Integrating AppDelegate

It is still possible to use AppDelegate for any legacy functionality with the new SwiftUI lifecycle. Below are the multiple methods of integrating AppDelegate code into a SwiftUI app.

 

1. Using the @UIApplicationDelegateAdaptor Property Wrapper

 SwiftUI itself has provided the @UIApplicationDelegateAdaptor property wrapper to serve between the two lifecycle approaches, new and old or, more appropriately, traditional AppDelegate.

Step-by-Step Example:

1. Just create an AppDelegate class much like what you've done for any UIKit app:

import UIKitclass AppDelegate: NSObject, UIApplicationDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { print("AppDelegate: App launched") return true } func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { print("AppDelegate: Registered for remote notifications") }}

 

2. Now, attach your AppDelegate to your SwiftUI App struct using @UIApplicationDelegateAdaptor:

import SwiftUI@mainstruct MyApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } }}

 

Your AppDelegate class is now the handler for many app lifecycle events, such as push notification registrations and launch configuration.

 

2. Working directly with UIResponder and UIApplicationDelegate

If you have an app that relies extensively on AppDelegate logic, you may ignore the SwiftUI lifecycle altogether and dive into the more traditional UIKit approach below:

  • Have a@UIApplicationMain as the entry point of your app instead of @main.
  • Define the AppDelegate and configure your app entirely in UIKit. 

It shouldn't be done unless you want to go all the way back to UIKit compatibility reasons.

 

3. Handling SceneDelegate Code

If your app was using SceneDelegate, then you could replicate similar functionality in SwiftUI using onChange modifiers and environment values like scenePhase.

Example: Using onChange to Detect Scene Changes

import SwiftUI@mainstruct MyApp: App { @Environment(\.scenePhase) private var scenePhase var body: some Scene { WindowGroup { ContentView() .onChange(of: scenePhase) { newPhase in switch newPhase { case .active: print("App is active") case .inactive: print("App is inactive") case .background: print("App is in the background") default: print("App is in an unknown state") // Better to handle default case } } } }}

 

This handles all state changes without the use of a SceneDelegate.

When to Use AppDelegate in SwiftUI

Although the app lifecycle in SwiftUI is fairly sufficient for most cases, AppDelegate should be used in the following situations:

  • Push Notifications: Device token registration and notification handling.
  • Background Tasks: Custom behavior for tasks running in the background.
  • Third Party Libraries: Many SDKs, like Firebase, still rely on AppDelegate methods.
  • Deep Linking: Via URLs into your app.

Best Practices

  • Minimize AppDelegate Use: Where possible, use the lifecycle features from SwiftUI like onChange and @Environment.
  • Centralize Logic: Use AppDelegate only for its essential lifecycle task, with other logic delegated to a designated manager or view model to deal with.
  • Bring Up Test: Ensure compatibility with lifecycle events of SwiftUI to avoid illegal behavior when the app is invoked.

 

Conclusion

SwiftUI app lifecycle is going to be the future of iOS, but AppDelegate will always be here for legacy features and third-party integrations. Incorporate @UIApplicationDelegateAdaptor to include AppDelegate logic into your SwiftUI apps declaratively with no need to compromise any of the benefits a declarative structure grants.

That means you can create apps that are contemporary, functional, and poised to accommodate future improvements.

Ready to transform your business with our technology solutions? Contact Us  today to Leverage Our iOS Expertise.

0

iOS

Related Center Of Excellence