2

I wrote test code for NavigationStack. The behavior of the code is a two-step transition(ContentView -> SubsubTestView -> DetailView).

But I got an error when I have selected a name in SubsubTestView.

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

Is there anything wrong with the wrong part of the code?

  • ContentView.swift
import SwiftUI

class EnvObj: ObservableObject {
    @Published var users = [User(name: "a"), User(name: "b"), User(name: "c")]
}

struct User: Hashable, Identifiable, Equatable {
    var id = UUID()
    var name = ""
    
    static func == (lhs: User, rhs: User) -> Bool{
        return lhs.id == rhs.id
    }
}

struct ContentView: View {
    @EnvironmentObject var envObj: EnvObj
    @State var moveToSubsub = false
    
    var body: some View {
        NavigationStack {
            Button("To subsub") {
                moveToSubsub = true
            }
            .navigationDestination(isPresented: $moveToSubsub) {
                SubsubTestView(vm: VM(envObj: envObj))
            }
        }
    }
}

struct SubsubTestView: View {
    @EnvironmentObject var envObj: EnvObj
    @StateObject var vm: VM
    
    var body: some View {
        VStack {
            List(self.vm.envObj.users) { user in
                NavigationLink(value: user) {
                    Text(user.name)
                }
            }
            .navigationDestination(for: User.self) { user in
                DetailView(vm: VMD(envObj: envObj, selectedUser: user))
            }
        }
    }
}

class VM: ObservableObject {
    var envObj: EnvObj = .init()
    
    init(envObj: EnvObj) {
        self.envObj = envObj
    }
}

struct DetailView: View {
    @StateObject var vm: VMD
    
    var body: some View {
        VStack {
            TextField("Name: ", text: (self.$vm.selectedUser ?? User()).name)
            Text(self.vm.selectedUser?.name ?? User().name)
            Button("submit", action: self.vm.submit)
        }
    }
}

class VMD: ObservableObject {
    var envObj: EnvObj = .init()
    @Published var selectedUser: User?
    
    init(envObj: EnvObj, selectedUser: User? = nil) {
        self.envObj = envObj
        self.selectedUser = selectedUser
    }
    
    private(set) lazy var submit = {
        if let user = self.selectedUser {
            self.update(user: user)
        }
    }
    
    func update(user: User) {
        self.envObj.users = self.envObj.users.map {
            return $0 == user ? user : $0
        }
    }
}

func ??<T>(binding: Binding<T?>, fallback: T) -> Binding<T> {
    return Binding(get: {
        binding.wrappedValue ?? fallback
    }, set: {
        binding.wrappedValue = $0
    })
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
tbt
  • 399
  • 3
  • 16
  • I think you can reduce this further. In my experiments, I found that simply embedding a NavigationLink within another NavigationLink is enough to trigger this issue. See https://gist.github.com/mgadda/d9a5731ef5da1e5c96dc135c364bd345 – mgadda Dec 26 '22 at 18:12

0 Answers0