Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON decoder The data couldn’t be read because it isn’t in the correct format

Tags:

json

swiftui

I am new to this. Somehow I am able to understand how to do this. I am doing below, but it's giving error- The data couldn’t be read because it isn’t in the correct format.Can someone help me with this? I am stuck on this from past 4 days. I really appreciate.

import SwiftUI
import Foundation
import Combine

struct Movie: Decodable, Identifiable {

    var id: Int
    var video: String
    var vote_count: String
    var vote_average: String
    var title: String
    var release_date: String
    var original_language: String
    var original_title: String
}

struct MovieList: Decodable{
    var results: [Movie]

___________

class NetworkingManager : ObservableObject{
    var objectWillChange = PassthroughSubject<NetworkingManager, Never>()
    @Published var movies = [Movie]()

   init() {
      load()
   }

   func load(){
       let url = URL(string: "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=<HIDDEN>")!  

    URLSession.shared.dataTask(with: url){ (data, response, error) in

        do {
            if let d = data {
                let decodedLists = try JSONDecoder().decode([Movie].self, from: d)
                DispatchQueue.main.async {
                    self.movies = decodedLists
                }
            }else {
                print("No Data")
            }
        } catch {
            print (error.localizedDescription)
        }

            }.resume()

   }

} 

This is how the response looks like:

{
  "page": 1,
  "results": [
    {
      "id": 419704,
      "video": false,
      "vote_count": 1141,
      "vote_average": 6.2,
      "title": "Ad Astra",
      "release_date": "2019-09-17",
      "original_language": "en",
      "original_title": "Ad Astra",
      "genre_ids": [
        878
      ],
      "backdrop_path": "/5BwqwxMEjeFtdknRV792Svo0K1v.jpg",
      "adult": false,
      "overview": "An astronaut travels to the outer edges of the solar system to find his father and unravel a mystery that threatens the survival of Earth. In doing so, he uncovers secrets which challenge the nature of human existence and our place in the cosmos.",
      "poster_path": "/xJUILftRf6TJxloOgrilOTJfeOn.jpg",
      "popularity": 227.167,
      "media_type": "movie"
    },
    ]
}

Code should fetch the data and hold in it the array I created. So that I can use it to display in the front end.

like image 709
Akash Avatar asked Dec 13 '19 01:12

Akash


People also ask

What does the data couldn't be read because it isn't in the correct format mean?

It's likely the JSON (or whatever format you are reading) is invalid and doesn't match your data model.

What does JSON decoder do?

An object that decodes instances of a data type from JSON objects.


1 Answers

I had to consume the exact same API for a similar project and this is how I did it.

When calling:

let response = try JSONDecoder().decode(MovieResponse.self, from: data)

It needs to match the same properties that the JSON response returns.

Below you'll see a MovieResponse struct and the Movie class, which will list all of the properties and return types that the JSON response returns.

The type adopts Codable so that it's decodable using a JSONDecoder instance.

See this official example for more information regarding Codable.

A type that can convert itself into and out of an external representation.

Provided they match then the JSONDecoder() will work to decode the data.

ContentView.swift:

struct ContentView: View {
    @EnvironmentObject var movieViewModel: MovieListViewModel

    var body: some View {
        MovieList(movie: self.movieViewModel.movie)
    }
}

MovieListViewModel.swift:

public class MovieListViewModel: ObservableObject {
    public let objectWillChange = PassthroughSubject<MovieListViewModel, Never>()
    private var movieResults: [Movie] = []

    var movie: MovieResults = [Movie]() {
        didSet {
            objectWillChange.send(self)
        }
    }

    func load(url: String = "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=<HIDDEN>") {
        guard let url = URL(string: url) else { return }
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            do {
                guard let data = data else { return }
                let response = try JSONDecoder().decode(MovieResponse.self, from: data)

                DispatchQueue.main.async {
                    for movie in response.results {
                        self.movieResults.append(movie)
                    }

                    self.movie = self.movieResults

                    print("Finished loading Movies")
                }
            } catch {
                print("Failed to decode: ", error)
            }
        }.resume()
    }
}

MovieResponse.swift:

struct MovieResponse: Codable {
    var page: Int
    var total_results: Int
    var total_pages: Int
    var results: [Movie]
}

public class Movie: Codable, Identifiable {
    public var popularity: Float
    public var vote_count: Int
    public var video: Bool
    public var poster_path: String
    public var id: Int
    public var adult: Bool
    public var backdrop_path: String
    public var original_language: String
    public var original_title: String
    public var genre_ids: [Int]
    public var title: String
    public var vote_average: Float
    public var overview: String
    public var release_date: String

    enum CodingKeys: String, CodingKey {
        case popularity = "popularity"
        case vote_count = "vote_count"
        case video = "video"
        case poster_path = "poster_path"
        case id = "id"
        case adult = "adult"
        case backdrop_path = "backdrop_path"
        case original_language = "original_language"
        case original_title = "original_title"
        case genre_ids = "genre_ids"
        case title = "title"
        case vote_average = "vote_average"
        case overview = "overview"
        case release_date = "release_date"
    }

    public init(popularity: Float, vote_count: Int, video: Bool, poster_path: String, id: Int, adult: Bool, backdrop_path: String, original_language: String, original_title: String, genre_ids: [Int], title: String, vote_average: Float, overview: String, release_date: String) {
        self.popularity = popularity
        self.vote_count = vote_count
        self.video = video
        self.poster_path = poster_path
        self.id = id
        self.adult = adult
        self.backdrop_path = backdrop_path
        self.original_language = original_language
        self.original_title = original_title
        self.genre_ids = genre_ids
        self.title = title
        self.vote_average = vote_average
        self.overview = overview
        self.release_date = release_date
    }

    public init() {
        self.popularity = 0.0
        self.vote_count = 0
        self.video = false
        self.poster_path = ""
        self.id = 0
        self.adult = false
        self.backdrop_path = ""
        self.original_language = ""
        self.original_title = ""
        self.genre_ids = []
        self.title = ""
        self.vote_average = 0.0
        self.overview = ""
        self.release_date = ""
    }
}

public typealias MovieResults = [Movie]

MovieCellViewModel.swift:

public class MovieCellViewModel {
    private var movie: Movie

    public init(movie: Movie) {
        self.movie = movie
    }

    public func getTitle() -> String {
        return self.movie.title
    }

    // add more properties or functions here
}

MovieCell.swift:

struct MovieCell: View {
    var movieCellViewModel: MovieCellViewModel

    var body: some View {
        Text(self.movieCellViewModel.getTitle())
    }
}

MovieList.swift:

struct MovieList: View {
    @EnvironmentObject var movieViewModel: MovieListViewModel

    var movie: MovieResults

    var body: some View {
        List(self.movie) { movie in
            MovieCell(movieCellViewModel: MovieCellViewModel(movie: movie))
        }
    }
}
like image 52
fulvio Avatar answered Oct 23 '22 06:10

fulvio