Is there a way to handle events that were in UIApplicationDelegate or UISceneDelegate in SwiftUI App lifecycle?
SwiftUI App lifecycle
With ScenePhase you can access the different states of the app as done previously in SceneDelegate
Code Block @main struct TestApp: App { var body: some Scene { WindowGroup { ContentView() } .onChange(of: scenePhase) { phase in switch phase { case .active: print("Active") case .inactive: print("Inactive") case .background: print("Background") @unknown default: print("Unknown") } } } }
You can also use the @UIApplicationDelegateAdaptor property wrapper if you prefer the UIKit way.
Code Block swift @main struct SampleApp: App { @UIApplicationDelegateAdaptor var delegate: AppDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { print(#function) return true } }
@Volker88 is totally right, you have to add @Environment(\.scenePhase) var scenePhase. So the full example should be:
Code Block swift import SwiftUI @main struct TestApp: App { @Environment(\.scenePhase) var scenePhase var body: some Scene { WindowGroup { ContentView() } .onChange(of: scenePhase) { phase in switch phase { case .active: print("Active") case .inactive: print("Inactive") case .background: print("Background") @unknown default: print("Unknown") } } } }
I have tried the multiple options so far. That entire .onChange seems like a bolt-on afterthought to be honest. I am sure it will work great in the future, but for now, it's not only not being called when you follow the examples (you must put it in a View), but it only gets called on change, doesn't have the initial launch, final termination, and will not help transitions between scenes either.
So for the time being, the .onChange would be good for the View, but would get called independently on every single instance of the app, which is not good if you have a central model expecting one single call.
And as described by @mtsrodrigues, you can create an AppDelegate, which will work good. But you don't have access to the App stack, you must make it go through hoops to get it and get your model, which might not be what you actually want. To get non-hackish, you'd put your app model in your AppDelegate, and have your view model caches registering to your AppDelegate so they'd get called on events, which is not what is expected.
So far, the best solution I found was to go on the different sites of my model where the App-wide delegation happens, and add Notification observers for each site. Bonus is it makes your code truly modular. Malus is they haven't yet created a SwiftUI-compatible platform-agnostic version, so you must ifdef your way.
Here is my code in my AppEnvironment @State object, that's now residing as a singleton in my App. (Please note my final version has all this code - and much more - directly in store and photoLibrary as there's no reason to centralize it)
So for the time being, the .onChange would be good for the View, but would get called independently on every single instance of the app, which is not good if you have a central model expecting one single call.
And as described by @mtsrodrigues, you can create an AppDelegate, which will work good. But you don't have access to the App stack, you must make it go through hoops to get it and get your model, which might not be what you actually want. To get non-hackish, you'd put your app model in your AppDelegate, and have your view model caches registering to your AppDelegate so they'd get called on events, which is not what is expected.
So far, the best solution I found was to go on the different sites of my model where the App-wide delegation happens, and add Notification observers for each site. Bonus is it makes your code truly modular. Malus is they haven't yet created a SwiftUI-compatible platform-agnostic version, so you must ifdef your way.
Here is my code in my AppEnvironment @State object, that's now residing as a singleton in my App. (Please note my final version has all this code - and much more - directly in store and photoLibrary as there's no reason to centralize it)
Code Block swift #if os(macOS) NotificationCenter.default.addObserver( forName: NSApplication.didFinishLaunchingNotification, object: nil, queue: .main) { [weak self] notification in self?.store.enabled = true } NotificationCenter.default.addObserver( forName: NSApplication.willTerminateNotification, object: nil, queue: .main) { [weak self] notification in self?.store.enabled = false } #else NotificationCenter.default.addObserver( forName: UIApplication.didFinishLaunchingNotification, object: nil, queue: .main) { [weak self] notification in self?.store.enabled = true } NotificationCenter.default.addObserver( forName: UIApplication.willTerminateNotification, object: nil, queue: .main) { [weak self] notification in self?.store.enabled = false } NotificationCenter.default.addObserver( forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: .main) { [weak self] notification in self?.photoLibrary.clearCache() } #endif