← All daysDay 29 of 49 · Part 8: Data, persistence, and networking
Day 29

Loading data with async/await

These are the lesson notes for this day. Its build-along tutorial and flash cards have not been written yet.

Lesson 33

Loading data with async/await

URLSession has asynchronous methods: let (data, response) = try await URLSession.shared.data(from: url). In SwiftUI you start that work with the .task modifier, which runs when the view appears and is cancelled automatically when the view goes away. .task(id:) restarts the work whenever a value changes. Show progress and failure explicitly: describe the state of the screen with one enum — loading, loaded, failed — rather than with several Booleans that can contradict one another.

Coming from UIKit, this replaces completion handlers, DispatchQueue.main.async, and cancelling requests by hand in viewWillDisappear.

Example
struct TripsScreen: View {
    enum LoadState { case loading, loaded([Trip]), failed(String) }
    @State private var state = LoadState.loading
    let url: URL

    var body: some View {
        Group {
            switch state {
            case .loading:            ProgressView()
            case .failed(let reason): Text(reason)
            case .loaded(let trips):  List(trips) { Text($0.name) }
            }
        }
        .task {                                // starts on appear, cancelled on disappear
            do {
                let (data, _) = try await URLSession.shared.data(from: url)
                state = .loaded(try JSONDecoder().decode([Trip].self, from: data))
            } catch {
                state = .failed("Could not load trips.")
            }
        }
    }
}
Exercise

Fetch a list from a public API and display it, with a loading indicator, an error state that has a Retry button, and pull-to-refresh using .refreshable.

After this lesson you should be able to answer
  1. What does .task do that onAppear { Task { … } } does not?
  2. Why describe loading state with one enum instead of separate isLoading and error properties?
  3. What does .task(id:) do when the id changes?
Resources