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()
}
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.