iosswiftrestcodable

Confused with Codable


I'm trying to fetch movie data using The Movie Database API and display them on a table view, but my code isn't actually getting any data.

class MovieData: NSObject, Decodable {
    var movies: [Movie]?
    
    private enum CodingKeys: String, CodingKey {
        case movies = "results"
    }
}

I think it has something to do with my coding keys because the movie data I want to retrieve are within an array. Seen here. But I'm a little confused with how Codable works.

class Movie: NSObject, Decodable {

    var title: String?
    var year: String?
    var rate: Double?
    var posterImage: String?
    var overview: String?
        
    private enum MovieKeys: String, CodingKey {
        case title
        case overview
        case releaseDate = "release_date"
        case rate = "vote_average"
        case posterImage = "poster_path"
    }
    
    required init(from decoder: Decoder) throws {
        let movieContainer = try decoder.container(keyedBy: MovieKeys.self)
        
        // Get movie info
        title = try movieContainer.decode(String.self, forKey: .title)
        overview = try movieContainer.decode(String.self, forKey: .overview)
        posterImage = try movieContainer.decode(String.self, forKey: .posterImage)
        rate = try movieContainer.decode(Double.self, forKey: .rate)
        // releaseDate = try movieContainer.decode(String.self, forKey: .releaseDate)
               
    }
}

This is what I'm using to make the API call, so when the user loads into the view the API will fetch movie data and display it on a cell.

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let requestURL = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=\(apiKey)&language=en-US&page=1")
        if let requestURL = requestURL {
            Task {
                
                do {
                    let (data, response) = try await URLSession.shared.data(from: requestURL)
                    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                        throw MovieListError.invalidServerResponse
                    }
                    
                    let decoder = JSONDecoder()
                    let movie = try decoder.decode(Movie.self, from: data)
                    
                    print(data)
                    print(movie)
                }
                catch {
                    print(error)
                }
            }
        }  
    }

Solution

  • It seems your are trying to decode a wrong object. It should be MovieData.self not Movie.self. And then you can iterate over the array of Movie.

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let requestURL = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=\(apiKey)&language=en-US&page=1")
        if let requestURL = requestURL {
            Task {
                
                do {
                    let (data, response) = try await URLSession.shared.data(from: requestURL)
                    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                        throw MovieListError.invalidServerResponse
                    }
                    
                    let decoder = JSONDecoder()
                    // change Movie.self to MovieData.self
                    let movies = try decoder.decode(MovieData.self, from: data)
                    
                    print(data)
                    print(movies)
                }
                catch {
                    print(error)
                }
            }
        }
    }