skip to Main Content

I have an issue with NavigationStack and .navigationDestination. This is a slightly simplified version of a view body in my app.

VStack {
    // other views

    List {
        Section("Recent Activity") {
            ForEach(viewModel.posts) { post in
                NavigationLink(value: post) {
                    PostListItemView(post: post)
                }
            }
        }
        
        Section("Reading List") {
            ForEach(viewModel.books) { book in
                NavigationLink(value: book) {
                    ReadingListItemView(
                        title: book.title,
                        authors: book.authors,
                        familiarity: book.familiarity
                    )
                }
            }
        }
    }
}
.navigationDestination(for: Book.self) { book in
    BookDetailsView(book: book, familiarity: .constant(book.familiarity))
}
.navigationDestination(for: Post.self) { post in
    PostDetailView(post: post)
}

Both Book and Post conform to Hashable.
Tapping on a ReadingListItemView results in the following error message:

A NavigationLink is presenting a value of type “Book” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated.

Note: Links search for destinations in any surrounding NavigationStack, then within the same column of a NavigationSplitView.

The the view is a child of a Navigation Stack. There is no other navigationDestination(for: Book.self) in the stack. The tapping a PostListItemView in the adjacent section behaves as expected. Moving the navigationDestination to the relevant List or Section don’t improve the situation.

The only things I can think of that might be confusing the system are:

  1. The parent of the view code above is itself a list item, so perhaps list virtualization is a problem somehow.
  2. The root view is a TabView with three tabs. Each tab has its own navigation stack. An adjacent tab does have a navigationDestination(for: Book.self) in it.
  3. If I replace each tab’s NavigationStack with a single stack that wraps the whole TabView, navigation works better, but it still exhibits some strange pushing behavior where views are pushed onto the stack that weren’t requested AFAICT.

I’d appreciate any insight into what I’m missing.

2

Answers


  1. Chosen as BEST ANSWER

    My problem was ultimately that there were too many navigationDestination(for: Hashable, destination:) modifiers in the navigation stack in some circumstances. The problem was They would occasionally conflict with one another, resulting in strange error messages like:

    Update NavigationRequestObserver tried to update multiple times per frame.

    My ultimate solution was to move most of the navigationDestination modifiers nearer the root of the navigation stack, which made some of the more convoluted parts of my navigation hierarchy more obvious.


  2. Here is a functional MRE and it does work like expected. So the issue might be somewhere else in code you don’t show.

    struct Post: Identifiable, Hashable {
        let id = UUID()
        var message: String
    }
    
    struct Book: Identifiable, Hashable {
        let id = UUID()
        var title: String
    }
    
    let viewModelposts = [
        Post(message: "Message One"),
        Post(message: "Message Two"),
        Post(message: "Message Three")
    ]
    
    let viewModelbooks = [
        Book(title: "Book One"),
        Book(title: "Book Two"),
        Book(title: "Book Three")
    ]
    
    
    struct ContentView: View {
        
        var body: some View {
            NavigationStack {
                ChildView()
            }
        }
    }
    
    struct ChildView: View {
        var body: some View {
            VStack {
                // other views
                List {
                    Section("Recent Activity") {
                        ForEach(viewModelposts) { post in
                            NavigationLink(value: post) {
                                //PostListItemView(post: post)
                                Text(post.message)
                            }
                        }
                    }
                    
                    Section("Reading List") {
                        ForEach(viewModelbooks) { book in
                            NavigationLink(value: book) {
                                //ReadingListItemView(title: book.title, authors: book.authors, familiarity: book.familiarity)
                                Text(book.title)
                            }
                        }
                    }
                }
            }
            .navigationDestination(for: Book.self) { book in
                //BookDetailsView(book: book, familiarity: .constant(book.familiarity))
                Text("This is details for book: (book.title)")
            }
            .navigationDestination(for: Post.self) { post in
                //PostDetailView(post: post)
                Text("This is details for Post: (post.message)")
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search