Search code examples
iosswiftuiswiftui-listswiftui-navigationlink

SwiftUI How to not duplicate ViewModel in List


I have simple 2 screens app. First screen is the List, second is detailed screen.

It's obvious I don't need 20 detailed screens, same as number of items in the List, but only one.

I need pass selected item as parameter to ViewModel of the detailed screen. I know two options implement this:

  1. Create for each NavigationLink new ViewModel(selectededItem: Item)
  2. Pass it through EnvironmentObject, but it's also bad solution, because first I need some how do like viewModel.selectedItem = item

My current implementation. It duplicates view models, also very important question is - does SwiftUI create copy of new view for each navigation link as well as view model?

List(viewModel.state.subCategory) { subCategory in
    HStack {
        NavigationLink(destination: {
            ProductsListBuilder.build(id: subCategory.id ?? -1)
        }, label: {
            Text(subCategory.title ?? "no name")
        })
    }
}

class ProductsListBuilder {
    static func build(id: Int) -> some View {
        let viewModel = ProductsViewModel(dataFetcher: RemoteDataProviderImpl.shared, productId: id)
        return ProductsListView(viewModel: viewModel)
    }
}

Solution

  • In such cases you can use LazyView:

    struct LazyView<Content: View>: View {
        let build: () -> Content
        init(_ build: @autoclosure @escaping () -> Content) {
            self.build = build
        }
        var body: Content {
            build()
        }
    }
    

    It makes build to only be called when the navigation link is actually opened:

    NavigationLink(destination: LazyView(
        ProductsListBuilder.build(id: subCategory.id ?? -1)
    ), label: {
        Text(subCategory.title ?? "no name")
    })