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?
لم تجد الفلم أو المسلسل ؟ سجل دخولك و انشئها
هل تريد تقييم او اضافة هذا العنصر للقائمة؟
لست عضو؟
رد بواسطة ticao2 🇧🇷 pt-BR
بتاريخ يوليو 10, 2023 في 2: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
رد بواسطة Majinnn
بتاريخ يوليو 11, 2023 في 4:15 صباحا
Ah my bad. It is in swiftUI.