Using view models in SwiftUI without allocation overhead

369 views Asked by At

Using View Models in SwiftUI

For a clean separation of concerns, I want to use my SwiftUI views with view models.

I have two views, a ListView and a DetailView where the first pushes the latter when any list item is tapped – a classic navigation view.

I define a viewModel property on my DetailView as follows and mark it as an observed object in order to update the view when any of its published properties change:

struct DetailView: View {
    @ObservedObject var viewModel: DetailViewModel

    var body: some View { 
        Text(viewModel.text)
    }
}

This approach requires me to pass a viewModel to the DetailView's initializer when I create the view inside the ListView:

struct ListView: View {
    let animals = ["", "", "", "", ""]

    var body: some View {
        List {
            ForEach(animals, id: \.self) { animal in
                NavigationLink(
                    destination: DetailView(viewModel: DetailViewModel(text: animal))
                ) {
                    Text(animal)
                }
            }
        }
    }
}

where DetailViewModel here is simply defined as follows:

class DetailViewModel: ObservableObject {
    @Published var text: String
}

(A view model is certainly over-engineered here, but it's just for the sake of explaining the problem.)

⚡️ The Problem

As the destination views for all navigations links are created each time the list view's body is updated, so are the respective view models. That's not much of a problem for a simple view model as shown above, but it certainly becomes a problem with more complex real-world view models. For example, a view model might instantiate other classes that are rather expensive to initialize. This would be done at the time of showing the ListView for each and every DetailView (model) – regardless of whether it's ever going to be shown (needed) or not.

Thus, it seems to me like an anti-pattern to create view models inside the bodies of other views. Still, it is the most common practice I've seen in code examples and articles across the Internet.

❓ My Question

Is there a way to keep a SwiftUI view from instantiating the destinations views of NavigationLinks immediately? Or is there a way to create view models for view to be used as navigation link destinations outside of a view's body?

0

There are 0 answers