SwiftUI 5.5 API Data to List View

Search for a command to run...

No comments yet. Be the first to comment.
By employing clean architecture , you can design applications with very low coupling and independent of technical implementation details, such as databases and frameworks. That way, the application becomes easy to maintain and flexible to change. It...
EnvironmentObject is useful when you want to create a dependency in a higher component of the layout tree and use it on a lower component without having to pass it down the tree through every child component. We will now use EnvironmentObject to moni...

CompositionLocal is useful when you want to create a dependency in a higher node of the layout tree and use it on a lower node without having to pass it down the tree through every child Composable. Here we will use it to direct our application when ...

The TDD (Test Driven Development) approach is important when it comes to testing small portions of business logic code in isolation. In this blog, I'll cover how I would use TDD to build a clean architecture To-do SwiftUI application I'll cover imp...

The TDD (Test Driven Development) approach is important when it comes to testing small portions of business logic code in isolation. In this blog, I'll cover how I would use TDD to build a clean architecture To-do Jetpack Compose Android application ...

The Room persistence library provides an abstraction layer over SQLite. To use Room in your app, add the following dependencies to your app’s build.gradle file: dependencies { .... kapt "org.xerial:sqlite-jdbc:3.34.0" // Room def roo...

Many times we need to make API calls to fetch data to display in a list. Here, I show how to do that with SwiftUI. To illustrate the structure of the application, let’s look at the following diagram:

We need to create an API service to send the network requests. We will use URLSession. Here we have one method to fetch all todo item and deserialise them to [TodoItem]
import Foundation
struct TodoItem: Identifiable, Codable {
let id: Int
let title: String
let completed: Bool
}
enum APIError: Error{
case invalidUrl, requestError, decodingError, statusNotOk
}
let BASE_URL: String = "https://jsonplaceholder.typicode.com"
struct APIService {
func getTodos() async throws -> [TodoItem] {
guard let url = URL(string: "\(BASE_URL)/todos") else{
throw APIError.invalidUrl
}
guard let (data, response) = try? await URLSession.shared.data(from: url) else{
throw APIError.requestError
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else{
throw APIError.statusNotOk
}
guard let result = try? JSONDecoder().decode([TodoItem].self, from: data) else {
throw APIError.decodingError
}
return result
}
}
The view model in turn uses the API Service to fetch todos to then publish
import Foundation
@MainActor
class TodoViewModel: ObservableObject {
@Published var todos: [TodoItem] = []
@Published var errorMessage = ""
@Published var hasError = false
func getTodos() async {
guard let data = try? await APIService().getTodos() else {
self.todos = []
self.hasError = true
self.errorMessage = "Server Error"
return
}
self.todos = data
}
}
Finally we have the view which watches the ViewModel for any todo list state changes. The todo list is display in the view. On list appear the view makes the api call.
import SwiftUI
struct TodoList: View {
@StateObject var vm = TodoViewModel()
var body: some View {
List{
ForEach(vm.todos){todo in
HStack{
Image(systemName: todo.completed ? "checkmark.circle": "circle")
.foregroundColor(todo.completed ? .green : .red)
Text("\(todo.title)")
}
}
}
.task {
await vm.getTodos()
}
.listStyle(PlainListStyle())
.navigationTitle("Todos")
}
}
