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.