Async Image in SwiftUI

If your app needs to display images that are hosted on a server, this post is for you. It’s a common scenario where images aren’t downloaded or are not present in the application’s assets. Another common issue is displaying a progress indicator until the image is fully displayed. AsyncImage is the solution to all these requests.

IIn this post, I use as a starting point the code discussed in a previous post (https://nicoladefilippo.com/pull-refresh-in-swiftui/), where we loaded a list of beer names. Now, we will also add images. Here is the code:

struct Beer: Codable, Identifiable {
    var id: Int
    var name: String
}

struct ContentView: View {
    @State var beers: [Beer] = []
    @State var page = 0
    var body: some View {
        NavigationStack {
            List(beers) { beer in
                Text(beer.name)
            }.refreshable {
                await getBeers()
            }
        }.onAppear {
            Task {
                await getBeers()
            }
        }
    }
    
    func getBeers() async {
        do {
            page += 1
            let url = URL(string: "https://api.punkapi.com/v2/beers?page=\(page)&per_page=30")!
            let (data, _) = try await URLSession.shared.data(from: url)
            let beersDownloaded = try JSONDecoder().decode([Beer].self, from: data)
            beers = beersDownloaded + beers
        } catch {
            print("Error")
        }
    }
}

To make improvements, first, add the image field to the Beer struct:

struct Beer: Codable, Identifiable {
    var id: Int
    var name: String
    var image_url: String
}

Now that we have the information, let’s take a look at how to display it:

AsyncImage(url: URL(string: beer.image_url)) { image in
         image.resizable().scaledToFit()
     } placeholder: {
         ProgressView()
     }.frame(width: 50, height: 50)

So, AsyncImage takes a URL, and until the image is displayed, a ProgressView is shown. Once the image becomes available, it’s resized proportionally to fit the size of the frame.

Now, let’s proceed with a list written in the following manner:

List(beers) { beer in
                HStack {
                    AsyncImage(url: URL(string: beer.image_url)) { image in
                        image.resizable().scaledToFit()
                    } placeholder: {
                        ProgressView()
                    }.frame(width: 50, height: 50)
                    Text(beer.name)
                }
            }

We have:

1 thought on “Async Image in SwiftUI”

Leave a Comment