Fetching Data: Learn how to fetch data from the internet and display it in your SwiftUI app.

Diego Perez Salas
3 min readApr 1, 2024

--

This article will guide you through the process, from making the network request to displaying the fetched data in your SwiftUI app.

Introduction to Fetching Data in SwiftUI

SwiftUI itself focuses on UI development and doesn’t directly provide networking capabilities. Instead, you use Swift’s URLSession for networking, alongside SwiftUI for displaying the data. Here's a step-by-step guide to fetching and displaying data from the internet in a SwiftUI app.

Step 1: Define Your Data Model

First, define a model that represents the data you’re fetching. It should conform to Decodable to easily decode from JSON.

struct Post: Decodable, Identifiable {
var userId: Int
var id: Int
var title: String
var body: String
}

Step 2: Create a ViewModel to Fetch Data

Create a ViewModel that fetches the data and publishes it for your views to display. Use @Published properties to automatically update your views when new data arrives.

import Foundation
import Combine

class PostsViewModel: ObservableObject {
@Published var posts: [Post] = []

func fetchPosts() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }

URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }

let posts = try? JSONDecoder().decode([Post].self, from: data)

DispatchQueue.main.async {
self.posts = posts ?? []
}
}.resume()
}
}

Step 3: Create a SwiftUI View to Display the Data

Now, create a SwiftUI view that uses your ViewModel to display the fetched data.

struct PostsView: View {
@StateObject private var viewModel = PostsViewModel()

var body: some View {
List(viewModel.posts) { post in
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
Text(post.body)
.font(.body)
}
}
.onAppear {
viewModel.fetchPosts()
}
}
}

Step 4: Testing Your App

Run your app to see the list of posts fetched from the internet and displayed in your UI. Ensure your app has internet access and the correct permissions to make network requests.

Fetching data from the internet in a SwiftUI app involves using URLSession to perform the network request, decoding the JSON into Swift models, and using @ObservableObject to bind the data to your UI. This pattern not only keeps your code clean and maintainable but also leverages SwiftUI's powerful data-driven UI updates. Remember to handle errors and loading states to provide a smooth user experience.

Error handling and user notification

Handling errors gracefully and notifying the user when something goes wrong is an important part of app development. Let’s modify our previous example to include error handling and user notification.

We’ll update the PostsViewModel to handle errors by showing an alert to the user when the fetch operation fails.

Step 1: Update the ViewModel

First, we’ll add an error property to the PostsViewModel to track when an error occurs. We'll also modify the fetchPosts function to update this property if an error is encountered during the network request.

class PostsViewModel: ObservableObject {
@Published var posts: [Post] = []
@Published var showAlert = false // To trigger an alert
@Published var errorMessage = "" // To store the error message

func fetchPosts() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }

URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
DispatchQueue.main.async {
if let error = error {
self?.errorMessage = error.localizedDescription
self?.showAlert = true
return
}

guard let data = data else {
self?.errorMessage = "Data is nil."
self?.showAlert = true
return
}

do {
let posts = try JSONDecoder().decode([Post].self, from: data)
self?.posts = posts
} catch {
self?.errorMessage = error.localizedDescription
self?.showAlert = true
}
}
}.resume()
}
}

Step 2: Update the ContentView

Next, we’ll modify the ContentView to show an alert when showAlert is true. We use the .alert modifier to present an alert dialog with the error message.

struct ContentView: View {
@StateObject private var viewModel = PostsViewModel()

var body: some View {
NavigationView {
List(viewModel.posts) { post in
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
Text(post.body)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.navigationTitle("Posts")
.onAppear {
viewModel.fetchPosts()
}
.alert(isPresented: $viewModel.showAlert) {
Alert(
title: Text("Error"),
message: Text(viewModel.errorMessage),
dismissButton: .default(Text("OK"))
)
}
}
}
}

With these changes, your app now handles errors that may occur during the data fetching process. If an error occurs, the user is notified with an alert showing a descriptive message. This approach helps improve the user experience by keeping the user informed about what’s happening and how they might be able to remedy any issues (e.g., checking their internet connection).

Remember, error handling and user communication are critical aspects of creating robust and user-friendly apps. Always aim to handle errors gracefully and keep your users informed.

Happy coding!

--

--