Pull refresh in SwiftUI

How many apps do you know that implement the pull-to-refresh feature? If you’re interested in learning how to create this helpful functionality in your app, you can read this post.

Let’s start with the basics:

struct ContentView: View {
    var body: some View {
        NavigationStack {
            List(1..<30) { n in
                Text("Beer \(n)")
            }
            .refreshable {
                print("Load other beer")
            }
        }
    }
}

This code displays a simple list of beers identified by name, with the important part being the refreshable feature. It’s this last feature that creates the graphical effect for the pull action and executes the code within the parentheses asynchronously (where the print instruction is located).

Real Example

Starting from this point, let’s try to create something a bit more complex and realistic. We aim to load beers, 30 per page, and when we perform the pull action, we want to download and display another 30 beers. The beers are sourced from: https://api.punkapi.com/v2/beers

First, create the struct for the beer, as we only want to retrieve the name:

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

The Beer struct needs to conform to the Codable protocol because we want to decode the JSON information. Additionally, it should implement the Identifiable protocol since we intend to display the beers in a list, which requires a unique identifier for each item.

Now implement the pull action:

struct ContentView: View {
    @State var beers: [Beer] = []
    @State var page = 0
    
    var body: some View {
        NavigationStack {
            List(beers) { beer in
                Text("Beer \(beer.name)")
            }
            .refreshable {
                await getBeers()
            }
        }.onAppear {
            Task {
                await getBeers()
            }
        }
    }
    func getBeers() async {
        // get the beers
    }
}

Thus, we declare an empty array that is filled with the initial set of beers when the view is displayed, and additional beers are downloaded when we perform the pull-to-refresh action. We also declare a page variable, which will be incremented with each request. Note how we need to declare a Task in the onAppear modifier to execute an asynchronous action. However, in the .refreshable modifier, we do not need to explicitly create a Task because it is designed to handle such actions directly.

Take a look to the getBeer function:

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 beersLoaded = try JSONDecoder().decode([Beer].self, from: data)
            beers = beersLoaded + beers
        } catch {
            print("Some error")
        }
    }

In this function, we increase the page number and use this number as a parameter in the URL to fetch 30 beers at a time. If there are no errors, the new beers are appended to the front of the beer array.

Drink in moderation!

Note: English is not my native language, so I apologize for any errors. I use AI solely to generate the banner of the post; the content is human-generated.

share this post with friends

Picture of Nicola De filippo

Nicola De filippo

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve

3 comments

  1. Greetings, I do believe your blog could be having browser compatibility issues.

    Whenever I look at your website in Safari, it looks fine
    however, when opening in I.E., it has some overlapping issues.
    I just wanted to give you a quick heads up! Apart from that,
    great blog!

Leave a comment

Your email address will not be published. Required fields are marked *

Who I am

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve.

Follow Me Here

Get The Latest Updates

Periodically receive my super contents on coding and programming

join the family;)

Recent Posts