The Movie Database Support

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?

2 replies (on page 1 of 1)

Jump to last post

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

Ah my bad. It is in swiftUI.

Can't find a movie or TV show? Login to create it.

Global

s focus the search bar
p open profile menu
esc close an open window
? open keyboard shortcut window

On media pages

b go back (or to parent when applicable)
e go to edit page

On TV season pages

(right arrow) go to next season
(left arrow) go to previous season

On TV episode pages

(right arrow) go to next episode
(left arrow) go to previous episode

On all image pages

a open add image window

On all edit pages

t open translation selector
ctrl+ s submit form

On discussion pages

n create new discussion
w toggle watching status
p toggle public/private
c toggle close/open
a open activity
r reply to discussion
l go to last reply
ctrl+ enter submit your message
(right arrow) next page
(left arrow) previous page

Settings

Want to rate or add this item to a list?

Login