Home > Net >  SwiftUI 4: Is it OK to make multiple .navigationDestination() calls at different levels in view hiea
SwiftUI 4: Is it OK to make multiple .navigationDestination() calls at different levels in view hiea

Time:07-14

I wonder if it's OK to call navigationDestination() multiple times at different levels in the view hierarchy? I googled but all the examples I found on the net called it on the top level view within NavigationStack. I tried the following code. It worked fine, but I'm not sure if it's by accident or by design. I'd like to code this way because it helps to organize the code.

struct ID<Value>: Hashable, Equatable {
    let uuid: UUID
    
    init() {
        uuid = UUID()
    }
}

typealias CategoryID = ID<Category>
typealias ItemID = ID<Item>

struct Catalog {
    var categories: [Category]
    
    func getCategory(_ categoryID: CategoryID) -> Category? {
        return categories.first { $0.id == categoryID }
    }
    
    func getItem(_ categoryID: CategoryID, _ itemID: ItemID) -> Item? {
        guard let category = getCategory(categoryID) else { return nil }
        return category.items.first { $0.id == itemID }
    }
}

struct Category: Identifiable {
    var id: CategoryID = .init()
    var name: String
    var items: [Item]
}

struct Item: Identifiable {
    var id: ItemID = .init()
    var name: String
}

func createTestData() -> Catalog {
    let categories = [
        Category(name: "Category A", items: [Item(name: "Item a1"), Item(name: "Item a2")]),
        Category(name: "Category B", items: [Item(name: "Item b1"), Item(name: "Item b2")])
    ]
    return Catalog(categories: categories)
}

struct ContentView: View {
    @State var catalog: Catalog = createTestData()
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(catalog.categories) { category in
                    NavigationLink(category.name, value: category.id)
                }
            }
            // This is the typical place to make one or multiple calls of navigationDestination().
            .navigationDestination(for: CategoryID.self) { categoryID in
                CategoryView(categoryID: categoryID, catalog: $catalog)
            }
        }
    }
}

struct CategoryView: View {
    let categoryID: CategoryID
    @Binding var catalog: Catalog
    
    var body: some View {
        if let category = catalog.getCategory(categoryID) {
            List {
                ForEach(category.items) { item in
                    NavigationLink(item.name, value: item.id)
                }
            }
            // Q: is it OK to call navigationDestination() here?
            .navigationDestination(for: ItemID.self) { itemID in
                ItemView(categoryID: categoryID, itemID: itemID, catalog: $catalog)

            }
        }
    }
}

struct ItemView: View {
    let categoryID: CategoryID
    let itemID: ItemID
    @Binding var catalog: Catalog

    var body: some View {
        if let item = catalog.getItem(categoryID, itemID) {
            Text(item.name)
        }
    }
}

Note the code uses a generic identifier type because otherwise SwifUI couldn't differentiate navigationDestination(for: CategoryID) and navigationDestination(for: ItemID).

CodePudding user response:

It is right in interface contract documentation ("to the stack", ie. entire stack, any place in the stack, except "lazy" as stated below):

demo

  • Related