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?