skip to Main Content

I have read the docs but I’m not understanding. Here is the code I have. I removed irrelevant lines.

It works right now. My question is, how do I programmatically navigate to a ThingDetail screen with a specific Thing passed in?

When the user receives a notification that says "tap to view this thing", I want them to tap it and then have the app launch to a specific ThingDetail(thing: thing), with a back button that takes them back to the list of all the things.

// Home screen is a list of Things that you can tap on to view the ThingDetail screen

@main
struct MyApp: App {
    @FetchRequest(sortDescriptors: []) var things: FetchedResults<Thing>
    @State private var navigationPath: NavigationPath
    
    var body: some Scene {
        WindowGroup {
            NavigationStack(path: $navigationPath) {
                ScrollView {
                    VStack {
                        ForEach(things) { thing in
                            ThingCard(thing: thing)
                        }
                    }
                }
                .navigationTitle("Things")
        }
    }
}

// This is a button that has the thing name and metadata, and links to the detail screen
struct ThingCard: View {
    let thing: Thing
    
    var body: some View {
        NavigationLink (destination: ThingDetailView(thing: thing)) {
            ZStack {
                RoundedRectangle()
                Text(thing.value ?? "")
            }
        }
    }
}

// This is the screen that you see when you tap on a ThingCard in the home screen list
struct ThingDetailView: View {
    let thing: Thing
    
    var body: some View {
        Text(thing.value ?? "")
    }
}

extension MyApp: UNCDelegateDelegate {
    func handleUserOpenedNotification(completionHandler: @escaping () -> Void) {
        // If user tapped notification that said "open to view this thing". I want to show the user a ThingDetail(thing: thing)
        completionHandler()
    }
}

2

Answers


  1. NavigationLink now takes a value and uses a corresponding .navigationDestination modifier (for each value type) when NavigationStack was added to iOS 16 in June 2022. This means it is possible for the navigation to stay active when the list is scrolled off the screen, e.g. by a change in a data or if using landscape split view on iPad. Before, when the navigation link scrolled off screen it deactivated and would pop the detail off – a really bad design flaw.

    var body: some View {
        NavigationStack {
            List {
                ForEach(things) { thing in
                     NavigationLink(thing.title, value: thing)
                 }
            }
            .navigationTitle("Things")
            .navigationDestination(for: Thing.self) { thing in
                 ThingDetail(thing: thing)
            }
        }
    }
    
    Login or Signup to reply.
  2. Try this solution, where moving the navigation path and UNUserNotificationCenterDelegate to the ObservableObject.

    import SwiftUI
    
    struct Thing: Identifiable, Equatable, Hashable {
        var id: String = UUID().uuidString
        var value: String?
    }
    
    class NotificationManager: NSObject, ObservableObject {
        
        @Published var navigationPath: NavigationPath = NavigationPath()
        @Published var things = [Thing(value: "Sample1"), Thing(value: "Sample2")]
        
        func get(thing id: String) -> Thing? {
            return things.first(where: { $0.id == id })
        }
    }
    
    extension NotificationManager: UNUserNotificationCenterDelegate {
        func userNotificationCenter(_ center: UNUserNotificationCenter,
                                    willPresent notification: UNNotification,
                                    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
            //App is in foreground
            //do whatever you want here, for example:
            navigationPath.append(Thing(value: "All new value"))
            completionHandler([.sound])
        }
    
        func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    
            //App is in background, user has tapped on the notification
            //do whatever you want here, for example:
            navigationPath.append(Thing(value: "All new value"))
            completionHandler()
        }
    }
    
    @main
    struct MyApp: App {
        
        private let notificationManager = NotificationManager()
        
        init() {
            UNUserNotificationCenter.current().delegate = notificationManager
        }
        
        var body: some Scene {
            WindowGroup {
                NavigationStack {
                    ContentView()
                        .environmentObject(notificationManager)
                }
            }
        }
    }
    
    struct ContentView: View {
        @EnvironmentObject private var notificationManager: NotificationManager
    
        var body: some View {
            NavigationStack(path: $notificationManager.navigationPath) {
                ScrollView {
                    VStack {
                        ForEach(notificationManager.things) { thing in
                            ThingCard(thing: thing)
                        }
                    }
                }
                .navigationTitle("Things")
                .navigationDestination(for: Thing.self) { thing in
                    ThingDetailView(thing: thing)
                }
            }
        }
    }
    
    struct ThingCard: View {
        let thing: Thing
        
        var body: some View {
            NavigationLink(value: thing) {
                ZStack {
                    Text(thing.value ?? "")
                }
            }
        }
    }
    
    struct ThingDetailView: View {
        let thing: Thing
        
        var body: some View {
            Text(thing.value ?? "")
        }
    }
    

    Result :

    GIF_description

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search