'Accessing AppState in AppDelegate with SwiftUI's new iOS 14 life cycle

I'm using SwiftUI's new app lifecycle coming in iOS 14.

However, I'm stuck at how to access my AppState (single source of truth) object in the AppDelegate. I need the AppDelegate to run code on startup and register for notifications (didFinishLaunchingWithOptions, didRegisterForRemoteNotificationsWithDeviceToken, didReceiveRemoteNotification) etc.

I am aware of @UIApplicationDelegateAdaptor but then I can not e.g. pass an object through to the AppDelegate with a constructor. I guess the other way round (creating the AppState in the AppDelegate and then accessing it in MyApp) does not work either.

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

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        // ...and access appState here
    }
}
class AppState: ObservableObject {
    // Singe source of truth...
    @Published var user: User()
}

Any help is appreciated. Maybe there is currently no way to achieve this, and I need to convert my app to use the old UIKit lifecycle?



Solution 1:[1]

Use shared instance for AppState

class AppState: ObservableObject {
    static let shared = AppState()    // << here !!

    // Singe source of truth...
    @Published var user = User()
}

so you can use it everywhere

struct MyApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @StateObject var appState = AppState.shared

    // ... other code
}

and

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        // ...and access appState here

        AppState.shared.user = ...
    }

backup

Solution 2:[2]

Is there a reason why you need to run the code in app delegate? If you are using new app lifecycle, why not trigger your code from WindowGroup.onChange()

struct MyScene: Scene {
    @Environment(\.scenePhase) private var scenePhase
    @StateObject private var cache = DataCache()

    var body: some Scene {
        WindowGroup {
            MyRootView()
        }
        .onChange(of: scenePhase) { newScenePhase in
            if newScenePhase == .background {
                cache.empty()
            }
        }
    }
}

Apple Documentation Link

Managing scenes in SwiftUI by Majid

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 svena