jsonswiftparsing

How to parse JSON starting with a "{"?


I'm using themovieDB API to parse a JSON. The JSON starts with { so it doesn't have any root or even though it has I don't know how to refer to that in Swift. When I try to parse it with a root that I created it gives me the error of:

"keyNotFound(CodingKeys(stringValue: "details", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "details", intValue: nil) ("details").", underlyingError: nil))"

How can I parse this JSON?

JSON Response:

{
    "id": 328,
    "name": "Jurassic Park Collection",
    "overview": "An American science fiction adventure film series based on the novel of the same name by Michael Crichton. The films center on the fictional Isla Nublar near Costa Rica in the Central American Pacific Coast, where a billionaire philanthropist and a small team of genetic scientists have created an amusement park of cloned dinosaurs.",
    "poster_path": "/jcUXVtJ6s0NG0EaxllQCAUtqdr0.jpg",
    "backdrop_path": "/njFixYzIxX8jsn6KMSEtAzi4avi.jpg",
    "parts": [
        {
            "adult": false,
            "backdrop_path": "/79bJL9ydAMYVltuNTt4VhxORqIz.jpg",
            "id": 329,
            "title": "Jurassic Park",
            "original_language": "en",
            "original_title": "Jurassic Par",
            "overview": "A wealthy entrepreneur secretly creates a theme park featuring living dinosaurs drawn from prehistoric DNA. Before opening day, he invites a team of experts and his two eager grandchildren to experience the park and help calm anxious investors. However, the park is anything but amusing as the security systems go off-line and the dinosaurs escape.",
            "poster_path": "/oU7Oq2kFAAlGqbU4VoAE36g4hoI.jpg",
            "media_type": "movie",
            "genre_ids": [
                12,
                878
            ],
            "popularity": 27.914,
            "release_date": "1993-06-11",
            "video": false,
            "vote_average": 7.9,
            "vote_count": 15644
        },

My code:

struct MovieFinder: Decodable {
    let details : [MovieModel]
}
struct MovieModel: Decodable, Identifiable {
    
    var id: Int
    var name: String
    var overview: String
    var posterPath: String
    var parts: Part
    
    enum CodingKeys: String, CodingKey {
        
        case posterPath = "poster_path"
        
        case id
        case name
        case overview
        case parts
        
    }
    
}

struct Part: Decodable, Identifiable {
    
    var id: Int
    var title: String
    var overview: String
    var posterPath: String
    var releaseDate: String
    var voteAverage: Double
    
    enum CodingKeys: String, CodingKey {
        
        case posterPath = "poster_path"
        case releaseDate = "release_date"
        case voteAverage = "vote_average"
        
        case id
        case title
        case overview
        
    }
    
}
struct MovieAPIManager {
    
    let apiKey =
    
    func MovieSearch() async -> [MovieModel] {
        
        if let url = URL(string: "https://api.themoviedb.org/3/collection/328-jurassic-park-collection?api_key=apiKey") {
            
            let request = URLRequest(url: url)
            
            do {
                
                let (data, response) = try await URLSession.shared.data(for: request)
                
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .iso8601
                let result = try decoder.decode(MovieFinder.self, from: data)
                return result.details
            } catch {
                print(error)
            }
        }
        return [MovieModel]()
    }
}

Content View:

struct MoviesCard: View {
    
    @State var movies = [MovieModel]()
    var dataService = MovieAPIManager()
    
    var body: some View {
        NavigationView {
            VStack {
                List(movies) { movie in
                    VStack {
                        Text(movie.parts.title)
                    }
                }
                .task {
                    movies = await dataService.MovieSearch()
                }
            }
        }
    }
}

#Preview {
    MoviesCard()
}

Solution

  • Try these models to decode your JSON data from the server.

    struct MovieFinder: Identifiable, Codable {
        let id: Int
        let name, overview, posterPath, backdropPath: String
        let parts: [Movie]
    
        enum CodingKeys: String, CodingKey {
            case id, name, overview, parts
            case posterPath = "poster_path"
            case backdropPath = "backdrop_path"
        }
    }
    
    struct Movie: Identifiable, Codable {
        let id: Int
        let adult, video: Bool
        let title, originalLanguage, originalTitle, overview: String
        let posterPath, mediaType, releaseDate: String
        let backdropPath: String?
        let genreIDS: [Int]
        let popularity, voteAverage: Double
        let voteCount: Int
    
        enum CodingKeys: String, CodingKey {
            case adult,id, title,overview,popularity,video
            case backdropPath = "backdrop_path"
            case originalLanguage = "original_language"
            case originalTitle = "original_title"
            case posterPath = "poster_path"
            case mediaType = "media_type"
            case genreIDS = "genre_ids"
            case releaseDate = "release_date"
            case voteAverage = "vote_average"
            case voteCount = "vote_count"
        }
    }
    

    Adjust the rest of your code accordingly.

    EDIT-1:

    here is my full test code that works for me:

    struct MoviesCard: View {
        @State var movies = [Movie]()
        var dataService = MovieAPIManager()
        
        var body: some View {
            NavigationView {
                VStack {
                    List(movies) { movie in
                        VStack {
                            Text(movie.title)
                        }
                    }
                    .task {
                        movies = await dataService.MovieSearch()
                    }
                }
            }
        }
    }
    
    struct MovieAPIManager {
        let apikey = "YOUR-APIKEY"
        
        func MovieSearch() async -> [Movie] {
            if let url = URL(string: "https://api.themoviedb.org/3/collection/328-jurassic-park-collection?api_key=\(apikey)") {
                
                let request = URLRequest(url: url)
    
                do {
                    let (data, response) = try await URLSession.shared.data(for: request)
                    let decoder = JSONDecoder()
                    decoder.dateDecodingStrategy = .iso8601
                    let result = try decoder.decode(MovieFinder.self, from: data)
                    return result.parts
                } catch {
                    print(error)
                }
            }
            return [Movie]()
        }
    }
    
    struct ContentView: View {
        var body: some View {
            MoviesCard()
        }
    }
    

    You will need to consult the docs to determine which properties are optional, and adjust the models by adding ? to those.