Previously we've discussed the issue of SwiftUI eagerly creating views when using NavigationLink with the destination
parameter, and how to work around that using NavigationLink with the value
parameter as introduced in macOS 13/iOS 16. For applications deploying on earlier versions of those operating systems , the .onAppear
view modifier is a great a work around. It allows you to perform more costly computing tasks only when the view has been displayed.
Let's look at how we can use the .task
view modifier to further break a network call out from the view's view model.
As an example, I've created a branch on the MealDBDownloadAsync example discussed in Interacting with Endpoints to take advantage of the already implemented endpoint fetching code.
I've added source code for this article to a branch of the existing for the MealDBDownloadAsync example available on GitHub.
Changes to the View Model
In the previous version of the view model the init function was fetching the data from the network as shown in Listing 1.
While this is an acceptable approach in this very simple case, if the view model was doing additional initialization it will cause that initialization to be delayed.
We can instead move that Task
from the view model to the SwiftUI view code. The new initializer is shown in Listing 2.
Again, because this example is very simple, that leaves our init function empty, but a practical view model would do more initialization here.
Updating the CategoryView
The CategoryView
now needs to execute the fetchCategories()
function. We'll have it do that in a .task
view modifier. This approach doesn't require creating an explicit Task
, which having the network fetch in the view model did. The updated CategoryView
is shown in Listing 3.
The only necessary changes were adding lines 12-14 that add the .task
view modifier and call the fetchCategories()
function through the viewModel
.
The .task
view modifier is executed only when the ScrollView
is displayed, which happens after the CategoryViewModel
has already been initialized.
.task
view modifier, not the implementation. This helps isolate view code from networking code.Summary
The .task
view modifier is not a replacement for using the .onAppear
view modifier to postpone costly initialization until necessary. It is, however, often more appreciate if a portion of that initialization that is asynchronous can be broken out of the view model initialization.
I'd love to hear your comments. Feel free to reach out to me at @sanguish on Mastodon.