- Published on
Adding an App Delegate to a SwiftUI App
- Authors
- Name
- Mick MacCallum
- @0x7fs
SwiftUI's App protocol provides a modern way to structure your app, but sometimes you need access to UIKit's App Delegate for handling app lifecycle events, push notifications, deep links, or other system callbacks. This article shows you how to integrate an App Delegate into your SwiftUI app.
Why Use App Delegate in SwiftUI?
While SwiftUI's App
protocol handles basic app lifecycle, there are scenarios where you need the App Delegate:
- Push Notifications: Registering for remote notifications and handling token updates
- Deep Links: Processing URL schemes and universal links
- Background App Refresh: Managing background tasks and app state
- Third-party SDKs: Many libraries still require App Delegate integration (usually for initialization in the
application(_:didFinishLaunchingWithOptions:)
method) - System Callbacks: Handling memory warnings, state restoration, etc.
Basic App Delegate Integration
The key is using the @UIApplicationDelegateAdaptor
property wrapper in your SwiftUI app to obtain a reference to an instance of the App Delegate.
import SwiftUI
import UIKit
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// Configure your app here
print("App launched")
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Handle push notification registration
let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
print("Device token: \(tokenString)")
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register for notifications: \(error)")
}
}
Scene Delegate Integration (Bonus)
For more complex apps that need scene-based lifecycle management, it is also necessary to add an App Delegate as a prerequisite for the Scene Delegate. Implment your App Delegate with the configurationForConnecting
method to return a UISceneConfiguration
with the delegateClass
set to the Scene Delegate class.
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { oldPhase, newPhase in
switch newPhase {
case .active:
print("App became active")
case .inactive:
print("App became inactive")
case .background:
print("App entered background")
@unknown default:
break
}
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let sceneConfig = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
sceneConfig.delegateClass = SceneDelegate.self
return sceneConfig
}
}
class SceneDelegate: NSObject, UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Handle scene connection
print("Scene will connect")
}
func sceneDidDisconnect(_ scene: UIScene) {
print("Scene did disconnect")
}
func sceneDidBecomeActive(_ scene: UIScene) {
print("Scene became active")
}
func sceneWillResignActive(_ scene: UIScene) {
print("Scene will resign active")
}
func sceneWillEnterForeground(_ scene: UIScene) {
print("Scene will enter foreground")
}
func sceneDidEnterBackground(_ scene: UIScene) {
print("Scene did enter background")
}
}
Best Practices
- Keep it minimal: Only implement the App Delegate methods you actually need.
- Use SwiftUI when possible: Prefer SwiftUI's lifecycle methods for simple cases
- Separate concerns: Create dedicated classes for different responsibilities (notifications, deep links, etc.)
- Test thoroughly: App Delegate methods are critical for app functionality