I create a pagination display utilizing TMDb api in SwiftUI for GridView :
var physique: some View {
ZStack {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: Constants.gridItemSize))]) {
ForEach(viewModel.films, id: .self.id) { film in
MovieColumn(film: film).onAppear {
if film == viewModel.films.final, viewModel.hasMoreData {
Activity {
await viewModel.loadMoreData()
}
}
}
}
}
if(!viewModel.films.isEmpty) {
Spacer()
loadingOrErrorView
}
}
if(viewModel.films.isEmpty) {
loadingOrErrorView
}
}.process {
await viewModel.loadMoreData()
}
}
@ViewBuilder
personal var loadingOrErrorView: some View {
swap viewModel.viewState {
case .loading:
ProgressView()
case .failure(let error):
errorView(error: error)
case .idle:
EmptyView()
}
}
personal func errorView(error: Error) -> some View {
VStack {
Textual content(error.localizedDescription)
.foregroundColor(.pink)
.multilineTextAlignment(.heart)
.padding()
Button(motion: {
Activity {
await viewModel.loadMoreData()
}
}) {
Textual content("Retry")
.padding()
.background(Colour.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
And right here is my ViewModel :
@Observable
class PaginationViewModel {
var films: [Movie] = []
var hasMoreData = true
var viewState: ViewState = .idle
@ObservationIgnored
personal var currentPage = 1
@ObservationIgnored
personal let networkService: NetworkService
init(networkService: NetworkService) {
self.networkService = networkService
}
@MainActor
func loadMoreData() async {
guard viewState != .loading && hasMoreData else { return }
self.viewState = .loading
do {
let request = TMDbRequest(path: .films, web page: currentPage)
let newItems: TMDbWrapper = attempt await networkService.carry out(request: request)
self.films.append(contentsOf: newItems.films)
self.currentPage += 1
self.hasMoreData = newItems.films.depend == Constants.PAGE_SIZE
self.viewState = .idle
} catch {
self.viewState = .failure(error: error)
}
}
personal struct Constants {
static let PAGE_SIZE = 20
}
}
enum ViewState: Equatable {
case idle
case loading
case failure(error: Error)
static func == (lhs: ViewState, rhs: ViewState) -> Bool {
swap (lhs, rhs) {
case (.idle, .idle): return true
case (.loading, .loading): return true
case (.failure(error: _), .failure(error: _)): return true
default: return false
}
}
}
As you see I’ve carried out loading and error view logic in two totally different place. One for preliminary state and one for loading subsequent web page. Is there any resolution to enhance this logic whereas utilizing GridView? I additionally surprise when you’ve got a extra optimize resolution for Pagination in SwiftUI?
Supply code : https://github.com/alirezaeiii/Pagination-Project/tree/fundamental