Hi, I'm trying to get random movies via your API, but I hit a rather annoying wall. I keep getting results from page 1, and only page 1. The call goes out smoothly; when I place the call in my browser I get the desired data, but in the app it reverts back to page 1. Is there something I am missing?
The API call + json decoder
func fetchTotalPages(genres: [Int], providers: [Int], completion: @escaping (Result<Int, MovieError>) -> Void) {
let genresString = genres.map { String($0) }.joined(separator: ",")
let providersString = providers.map { String($0) }.joined(separator: ",")
guard let encodedGenres = genresString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let encodedProviders = providersString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
completion(.failure(.invalidEndpoint))
return
}
let urlString = "https://api.themoviedb.org/3/discover/movie?api_key=\(apiKey)&include_adult=false&page=1&with_genres=\(encodedGenres)&watch_region=NL&vote_average.ite=1&with_watch_providers=\(encodedProviders)"
guard let url = URL(string: urlString) else {
completion(.failure(.invalidEndpoint))
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let apiResponse = try JSONDecoder().decode(APIResponse.self, from: data)
DispatchQueue.main.async {
self.totalPages = apiResponse.totalPages
completion(.success(apiResponse.totalPages))
print("total pages are: \(apiResponse.totalPages)")
}
} catch {
print("Error decoding API response: \(error)")
completion(.failure(.serializationError))
}
} else if let error = error {
print("Error fetching movies: \(error)")
completion(.failure(.invalidResponse))
}
}.resume()
print(urlString)
}
func discoverMovies(from endpoint: MovieListEndpoint, page: Int, genres: String, providers: String, completion: @escaping (Result<RandomMovieResponse, MovieError>) -> ()) {
guard let url = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=\(apiKey)&include_adult=false&page=\(page)&with_genres=\(genres)&watch_region=NL&vote_average.ite=1&with_watch_providers=\(providers)") else {
completion(.failure(.invalidEndpoint))
return
}
self.loadURLAndDecode(url: url, completion: completion)
print(url)
}
private func loadURLAndDecode<D: Decodable>(url: URL, params: [String: String]? = nil, completion: @escaping (Result<D, MovieError>) -> ()) {
guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
completion(.failure(.invalidEndpoint))
return
}
var queryItems = [URLQueryItem(name: "api_key", value: apiKey)]
if let params = params {
queryItems.append(contentsOf: params.map { URLQueryItem(name: $0.key, value: $0.value) })
}
urlComponents.queryItems = queryItems
guard let finalURL = urlComponents.url else {
completion(.failure(.invalidEndpoint))
return
}
urlSession.dataTask(with: finalURL) { [weak self] (data, response, error) in
guard let self = self else { return }
if error != nil {
self.executeCompletionHandlerInMainThread(with: .failure(.apiError), completion: completion)
return
}
guard let httpResponse = response as? HTTPURLResponse, 200..<300 ~= httpResponse.statusCode else {
self.executeCompletionHandlerInMainThread(with: .failure(.invalidResponse), completion: completion)
return
}
guard let data = data else {
self.executeCompletionHandlerInMainThread(with: .failure(.noData), completion: completion)
return
}
do {
let decodedResponse = try self.jsonDecoder.decode(D.self, from: data)
self.executeCompletionHandlerInMainThread(with: .success(decodedResponse), completion: completion)
} catch {
self.executeCompletionHandlerInMainThread(with: .failure(.serializationError), completion: completion)
}
}.resume()
}
private func executeCompletionHandlerInMainThread<D: Decodable>(with result: Result<D, MovieError>, completion: @escaping (Result<D, MovieError>) -> ()) {
DispatchQueue.main.async {
completion(result)
}
}
}
The function to generate random pages
func performTask(numOption: Int, selectedEndpoint: MovieListEndpoint, selectedGenresViewModel: SelectedGenresViewModel, selectedProviderViewModel: SelectedProviderViewModel, completion: @escaping ([Int], Int?) -> Void) {
let genreIDs = selectedGenresViewModel.selectedGenres.map { $0.id }
let providerIDs = Array(selectedProviderViewModel.selectedProvider.map { $0.provider_id }.shuffled().prefix(1))
RandomMovieStore.shared.fetchTotalPages(genres: genreIDs, providers: providerIDs) { result in
switch result {
case .success(let totalPages):
let pageNumbers = getRandomPageNumbers(numOption: numOption, totalPages: totalPages)
applyFiltersAndFindMovieIDs(selectedEndpoint: selectedEndpoint, pages: pageNumbers, genres: genreIDs, providers: providerIDs) { ids in
completion(ids, totalPages)
}
case .failure(let error):
print("Failed to fetch total pages: \(error)")
completion([], nil)
}
}
// kiest een random pagina nummer tussen 1 en de totaal pagina's
func getRandomPageNumbers(numOption: Int, totalPages: Int) -> [Int] {
let limitedTotalPages = min(totalPages, 50) // Limiet van 50 pagina's
let randomNumbers = Array(1...limitedTotalPages).shuffled().prefix(numOption)
print("number of options: \(numOption)")
print(randomNumbers)
return Array(randomNumbers)
}
// Voegt filters toe aan de API call (mocht die er zijn), voegt de pagina in de api call. Bij succes kiest die 1 random film van de resultaten en pakt de film id.
func applyFiltersAndFindMovieIDs(selectedEndpoint: MovieListEndpoint, pages: [Int], genres: [Int], providers: [Int], completion: @escaping ([Int]) -> Void) {
var movieIDs = [Int]()
let group = DispatchGroup()
for page in pages {
group.enter()
let genreIDs = genres.map(String.init).joined(separator: ",")
let providerIDs = Array(providers.shuffled().prefix(1)) // Limiet van 1 provider id per api call.
RandomMovieStore.shared.discoverMovies(from: selectedEndpoint, page: page, genres: genreIDs, providers: providerIDs.map(String.init).joined()) { result in
switch result {
case .success(let response):
print("this is page", page)
if !response.results.isEmpty {
print("API response:", response)
let randomIndex = Int.random(in: 1..<response.results.count)
print("this is randomIndex", randomIndex)
let randomMovie = response.results[randomIndex]
let randomMovieID = randomMovie.id
print("Random movie ID:", randomMovieID)
movieIDs.append(randomMovieID)
}
case .failure(let error):
print("API call failed with error: \(error)")
}
group.leave()
}
}
group.notify(queue: .main) {
print("All API calls completed")
completion(movieIDs)
}
}
}
Then the button that activates it
Button(action: {
Task {
performTask(
numOption: numOption, selectedEndpoint: selectedEndpoint,
selectedGenresViewModel: selectedGenresViewModel,
selectedProviderViewModel: selectedProviderViewModel
) { movieIDs,arg in
var fetchedMovies = [Movie]()
let dispatchGroup = DispatchGroup()
// plaatst de id van de films om de infromatie eruit te halen
for id in movieIDs {
dispatchGroup.enter()
print("Fetching movie with ID: \(id)")
MovieStore.shared.fetchMovie(id: id) { result in
switch result {
case .success(let movie):
fetchedMovies.append(movie)
case .failure(let error):
print(error.localizedDescription)
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
self.filteredMovies = fetchedMovies
self.movieTitles = fetchedMovies.map({ $0.title })
print(self.movieTitles)
}
}
}
It then fetches the movie via ID.
Is my code wrong or is there a reason I only get page 1?
¿No encuentras una película o serie? Inicia sesión para crearla:
¿Quieres puntuar o añadir este elemento a una lista?
¿No eres miembro?
Contestado por ticao2 🇧🇷 pt-BR
el 10 de julio de 2023 a las 14:15
I'm not a code programmer.
I can't even identify which programming language/tool you are using.
My advice is that you add the name of the language/tool in the title of this talk.
Perhaps that way it will more easily attract the attention of other users.
For example, if you are using JavaScript, leave the title like this:
JavaScript - not going past page 1
Contestado por Majinnn
el 11 de julio de 2023 a las 04:15
Ah my bad. It is in swiftUI.