Pagination is a core feature when working with large data sets in mobile apps, and SwiftUI offers powerful, yet simple tools to implement it effectively. In this comprehensive guide, we’ll go through how to use ScrollView, LazyVStack, and the MVVM architecture to create a paginated list.
By the end, you’ll have a SwiftUI view that loads data as you scroll, ensuring smooth performance and a great user experience. Let’s dive into implementing pagination in SwiftUI!
Pagination helps handle large datasets by breaking them into smaller, more manageable pages. Without pagination, loading an entire data set at once can lead to performance issues and longer loading times, especially on mobile devices.
By using SwiftUI pagination, you can load data incrementally, making your app more efficient and user-friendly.
Imagine an API that returns 1,000 records at once. Loading all records at once can overwhelm users and slow down performance. Instead, with pagination, we divide this response into 20 pages with 50 records each. This guide will demonstrate how to paginate an API response using SwiftUI’s ScrollView and LazyVStack with the MVVM architecture.
We’ll use the “LIST USERS” API from Reqres, a popular free API for testing, which provides total_pages
, per_page
, and page
parameters along with user data.
Using MVVM in SwiftUI helps keep your code clean and organized, especially when building paginated views. Here’s a breakdown of the MVVM components for pagination in SwiftUI:
Let’s start with our User
model, representing each user and their details from the API. This model will be our single source of truth.
struct User: Identifiable, Decodable {
let id: Int
let email: String
let first_name: String
let last_name: String
let avatar: String
}
struct UserResponse: Decodable {
let page: Int
let per_page: Int
let total: Int
let total_pages: Int
let data: [User]
}
The UserResponse
struct represents the complete response from the API, including pagination metadata like page
and total_pages
.
The ViewModel will manage pagination by tracking the current page and loading new pages as needed. SwiftUI ViewModel here ensures that the data updates are handled effectively, using @Published
properties to update the UI automatically.
import Combine
class UsersViewModel: ObservableObject {
@Published var users: [User] = []
private var currentPage = 1
private var totalPages = 1
private var isLoading = false
private var cancellables = Set<AnyCancellable>()
init() {
loadUsers()
}
func loadUsers() {
guard !isLoading && currentPage <= totalPages else { return }
isLoading = true
let urlString = "https://reqres.in/api/users?page=(currentPage)"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: UserResponse.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { _ in }, receiveValue: { [weak self] response in
self?.users.append(contentsOf: response.data)
self?.totalPages = response.total_pages
self?.currentPage += 1
self?.isLoading = false
})
.store(in: &cancellables)
}
}
@Published
and dataTaskPublisher
enables automatic SwiftUI view updates and efficient async loading.With our ViewModel in place, we can create the SwiftUI view to display the paginated list of users. Here, ScrollView
and LazyVStack
work together to create an infinite scroll effect.
import SwiftUI
struct UsersListView: View {
@StateObject private var viewModel = UsersViewModel()
var body: some View {
ScrollView {
LazyVStack {
ForEach(viewModel.users) { user in
UserView(user: user)
.onAppear {
if user == viewModel.users.last {
viewModel.loadUsers()
}
}
}
}
}
}
}
LazyVStack
ensures efficient memory usage by only loading views as they appear on the screen.onAppear
, we call loadUsers()
when the last item becomes visible, fetching the next page.The UserView
displays each user’s details, like their profile image, name, and email.
import SwiftUI
struct UserView: View {
let user: User
var body: some View {
HStack {
AsyncImage(url: URL(string: user.avatar))
.frame(width: 50, height: 50)
.clipShape(Circle())
VStack(alignment: .leading) {
Text("\(user.first_name) \(user.last_name)")
.font(.headline)
Text(user.email)
.font(.subheadline)
.foregroundColor(.gray)
}
.padding(.leading, 10)
}
.padding()
}
}
By using AsyncImage
, we load user profile images directly from the URL, making it a lightweight and efficient solution for profile images in a paginated list.
With our MVVM setup complete, launching the app will initially display a list of six users. As you scroll down, additional users load seamlessly, creating an infinite scroll effect until the final page of data is reached.
Using SwiftUI’s LazyVStack for infinite scrolling lists allows you to handle large data sets in a performant way. However, there are a few more things to consider for production-ready apps:
With this setup, you now have a scalable, efficient pagination implementation in SwiftUI using MVVM, ScrollView, and LazyVStack.
Hope you enjoyed the article. Happy coding! 👏
Don’t miss out on the latest updates and expert advice from Perisync. Subscribe to our blog and get fresh content delivered straight to your inbox, so you’re always informed and ready to take on the next big challenge.