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 NavigationLink
s 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
?