1

Consider the following code,

import SwiftUI

class Product: Identifiable {
    let id = UUID()
    let title: String

    init(title: String) {
        self.title = title
    }
}

struct ContentView: View {

    let products: [Product]

    init(products: [Product]) {
        self.products = products
    }

    var body: some View {

        NavigationStack {
            List(self.products) { product in
                NavigationLink(product.title, value: product)
                // compiler: "product has to conform to Hashable"
            }
            .navigationDestination(for: Product.self) { product in
                ProductDetailView(product: product)
            }
        }
    }

}

struct ProductDetailView: View {
    let product: Product

    init(product: Product) {
        self.product = product
    }

    var body: some View {
        Text(product.title)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(
            products: [
                Product(title: "Frisbee"),
                Product(title: "Bicycle")
            ])
    }
}

The compiler so far claims that Product has to conform to Hashable. I want to try to avoid slapping Hashable to the Product just to satisfy this particular use case. Is there any other way I could "in-line" tell the NavigationStack construct to consider the ID for hashing, for example? Similar to a ForEach where I can also tell the view which property to consider the id of each item to keep track of it.

I could, for example, instead of having the [Product] array, a Dictionary of [Product:ID = Product], but I still wouldn't know how to pass that to the NavigationLink or .navigationDestination since at this point I'd still end up with having only a Product which again won't satisfy Hashable ofc. // edit: Also decorating my Product with something like NavigatingProduct: Hashable wouldn't work I guess because of bindings later…

Having said this, why is there a need for Hashable altogether? Does SwiftUI do some serialization behind to build the breadcrumb for example, or keep a map of hashes to entities?

Thanks for your help!

Adrian Föder
  • 770
  • 1
  • 7
  • 22
  • 1
    You should Watch “Demystify SwiftUI”. Apple goes into detail there but in short no, you can’t and avoiding Hashable will make a SwiftUI app very inefficient also unless you are adopting ObservableObject your models should be a struct it makes conformance to Hashable, Equatable and Identifiable much simpler. – lorem ipsum Apr 20 '23 at 12:23
  • Why do you think Hashable doesn’t work with Bindings? It seems like you are fighting SwiftUI. You should look at the “Apple SwiftUI tutorials” too. SwiftUI is not like other UI frameworks. – lorem ipsum Apr 20 '23 at 12:31
  • regarding the Bindings I meant if I put a decorator around it like `struct NavigationProduct: Hashable { let product: Product }` and then `NavigationLink(product.title, value: NavigationProduct(product: product))` but this indeed doesn't work – Adrian Föder Apr 20 '23 at 12:58
  • 1
    No it wouldn’t, Bindings and navigation destination don’t work at all, there are a ton of workarounds out there but none of them work. If you need a Binding you have to use the NavigationLink with destination in the initializer. Maybe there will be a viable solution in June but as of now they are all just “hacks” that “work” under precarious setups. – lorem ipsum Apr 20 '23 at 13:04
  • @loremipsum: feel free to add the basic "you can't and you won't" as an answer that I can accept, so you get the reputation. It's a perfectly fine answer for me. – Adrian Föder Apr 20 '23 at 14:13

0 Answers0