← All courses

Networking in SwiftUI

🗓 May 31, 2026 ⏱ 1 min read

A view model

@MainActor
class UsersViewModel: ObservableObject {
    @Published var users: [User] = []
    @Published var isLoading = false
    @Published var error: String?

    func load() async {
        isLoading = true; error = nil
        do {
            let url = URL(string: "https://api.example.com/users")!
            let (data, _) = try await URLSession.shared.data(from: url)
            users = try JSONDecoder().decode([User].self, from: data)
        } catch {
            self.error = error.localizedDescription
        }
        isLoading = false
    }
}

Driving the UI

struct UsersView: View {
    @StateObject private var vm = UsersViewModel()
    var body: some View {
        Group {
            if vm.isLoading { ProgressView() }
            else if let e = vm.error { Text(e) }
            else { List(vm.users) { Text($0.name) } }
        }
        .task { await vm.load() }   // runs when the view appears
    }
}
Tip: .task { } ties async work to the view’s lifetime and cancels it automatically when the view disappears.