onChange not detecting AppDelegate Update in SwiftUI

Viewed 68

I have an AppDelegate in my SwiftUI app, and I set a ViewModel like so:

class AppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
    var AppNavigator: ApplicationNavigator = ApplicationNavigator()
}

This is my ApplicationNavigator View Model:

class ApplicationNavigator: ObservableObject {
    @Published var applicationNavigation: ApplicationNavigatorItem = ApplicationNavigatorItem(id: "default")
}

Where ApplicationNavigator takes an ApplicationNavigatorItem:

struct ApplicationNavigatorItem: Identifiable, Codable  {
    var id: String? = UUID().uuidString
}

And when a user taps on a remote notification, I update the AppNavigator in my AppDelegate with a new ApplicationNavigatorItem:

func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse,
                              withCompletionHandler completionHandler: @escaping () -> Void) {

    self.AppNavigator.applicationNavigation = ApplicationNavigatorItem(
        id: newUUID
    )
}

Now to the SwiftUI part.

In my views, I've shared appDelegate as an environment object with my view, since I want to listen for changes to appDelgate.AppNavigator.applicationNavigation.id.

I've set an onChanged modifier to my main view:

.onChange(of: appDelegate.AppNavigator.applicationNavigation.id) { _ in
    DispatchQueue.main.async {
        appDelegate.AppNavigator.navigate()
    }
}

And this only works if the user is tapping the push notification when the app is in the background.

However, if a user receives the push notification while they're inside the app, and it is active, nothing happens when they tap it.

Oddly enough, after tapping a notification while inside the app, (where nothing happens) if I swipe the app up until it's scenePhase is .inactive || .background, and re-open the app, the code inside .onChange(..) { _ in // code } finally runs.

I've even put a Text(appDelegate.AppNavigator.applicationNavigation.id!) on the main view to see if appDelegate.AppNavigator.applicationNavigation.id has changed, but it doesn't if I'm still inside the app.

I'm trying to send the user to a specific view if they tap on a notification, and sometimes they might be inside the app. Can anyone help me see where I've made a mistake?

1 Answers

Okay, I've 1. solved the problem, and 2. learned something very cool about SwiftUI!

Instead of initialising ApplicationNavigator inside of appDelegate, I can create an @ObservedObject in my view:

@ObservedObject var appNavigator = ApplicationNavigator.shared

And I can update my .onChange modifier to:

.onChange(of: appNavigator.applicationNavigation.id) { _ in
    DispatchQueue.main.async {
        // Do navigation things
    }
}

And then when I want to update the ApplicationNavigatorItem in my appDelegate, I can set it on ApplicationNavigator.shared:

ApplicationNavigator.shared.applicationNavigation = ApplicationNavigatorItem(id: newUUID)

I give thanks to this post that helped me out!

P.S. If you attach an .onAppear { } to your main view, wherever you detect the change, and run the code you execute in your .onChange , it will work even if the app is not open at all!

Related