0

So I read that it's not possible to get an array of pages from the API (like; 1...50 or so), you have to do it individually. But I'm lazy so I'mm trying to work around it. Here is the api call

func fetchMovies(from endpoint: MovieListEndpoint, page: Int, completion: @escaping (Result<MovieResponse, MovieError>) -> ()) {
    guard let url = URL(string: "\(baseAPIURL)/movie/\(endpoint.rawValue)?api_key=\(apiKey)&page=\(page)") else {
        completion(.failure(.invalidEndpoint))
        return
    }
    
    self.loadURLAndDecode(url: url) { [weak self] (result: Result<MovieResponse, MovieError>) in
        switch result {
        case .success(let movieResponse):
            self?.movies.append(contentsOf: movieResponse.results)
            
            // Increment the current page for the next fetch
            self?.currentPage += 1
            
            completion(.success(movieResponse))
            
        case .failure(let error):
            completion(.failure(error))
        }
    }
}

Here is the loading code

class MovieListState: ObservableObject {
@Published var movies: [Movie] = []
@Published var isLoading: Bool = false
@Published var error: MovieError?

private let movieService: MovieService
private var currentPage: Int = 1

init(movieService: MovieService = MovieStore.shared) {
    self.movieService = movieService
}

func fetchNextPage(with endpoint: MovieListEndpoint) {
    guard !isLoading else { return }
    currentPage += 1
    loadMovies(with: endpoint, page: currentPage)
}

func loadMovies(with endpoint: MovieListEndpoint, page: Int) {
    isLoading = true
    movieService.fetchMovies(from: endpoint, page: page) { [weak self] result in
        guard let self = self else { return }
        self.isLoading = false
        
        switch result {
        case .success(let response):
            self.movies.append(contentsOf: response.results)
            
        case .failure(let error):
            self.error = error
        }
    }
}

}

and lastly the view code

struct MovieFullListView: View {
@ObservedObject private var movieListState: MovieListState

let title: String
let endpoint: MovieListEndpoint

@State var selection = 0

@State private var isScrolledToEnd = false

var items: [GridItem] = [GridItem(.flexible(), spacing: 20), GridItem(.flexible(), spacing: 20)]

init(title: String, endpoint: MovieListEndpoint) {
    self.title = title
    self.endpoint = endpoint
    self.movieListState = MovieListState()
}

var body: some View {
    VStack(alignment: .leading, spacing: 16) {
        Text(title)
            .font(.title)
            .fontWeight(.bold)
            .padding(.horizontal)
        
        ScrollView(.vertical, showsIndicators: false) {
            LazyVGrid(columns: items, spacing: 30) {
                ForEach(movieListState.movies) { movie in
                    VStack {
                        NavigationLink(destination: MovieDetailView(movieId: movie.id)) {
                            MoviePosterCard(movie: movie)
                                .aspectRatio(contentMode: .fit)
                        }
                        .buttonStyle(.plain)
                        
                        HStack {
                            Spacer()
                            
                            Button(action: {
                                // Action code here
                            }) {
                                VStack {
                                    if selection == 0 {
                                        Image("Watched")
                                            .resizable()
                                            .frame(width: 28, height: 28)
                                    } else {
                                        Image("watch")
                                            .resizable()
                                            .frame(width: 28, height: 28)
                                    }
                                }
                            }
                            
                            Spacer()
                            
                            Button(action: {
                                // Action code here
                            }) {
                                VStack {
                                    if selection == 1 {
                                        Image("Saved")
                                            .resizable()
                                            .frame(width: 25, height: 25)
                                    } else {
                                        Image("Save")
                                            .resizable()
                                            .frame(width: 25, height: 25)
                                    }
                                }
                            }
                            
                            Spacer()
                        }
                        .padding(.bottom, 6)

                    }
                    .background(Color.gray.opacity(0.1))
                    .cornerRadius(10)
                }
            }
            Button(action: {
                movieListState.fetchNextPage(with: endpoint)
            }) {
                Text("Load More")
                    .font(.headline)
                    .foregroundColor(.blue)
                    .padding()
                    .background(Color.gray.opacity(0.1))
                    .cornerRadius(10)
            }
            .padding()
        }
    }
    .onAppear {
        movieListState.loadMovies(with: endpoint, page: 1)
    }
}

}

The problem is it doesn't fetch a new api call when I press the load more button. Any idea how to fix this?

  • What's happening? Is it executing & loading the previous page or is it not executing at all? – Timmy May 25 '23 at 11:07
  • It reloads the first page, giving me double entries of the first page, but it should keep the first page entries and load thee second page entries – Joost van Grieken May 25 '23 at 12:32
  • In `class MovieListState`, you could try using: `DispatchQueue.main.async { self.isLoading = false .... }` to have the updates done on the main thread for the UI. Note, it would be better to use `@StateObject var movieListState = MovieListState()` – workingdog support Ukraine May 26 '23 at 06:37

0 Answers0