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.
3 comments
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!
Thanks, I’ll check.