Warm tip: This article is reproduced from serverfault.com, please click

Swift, The data couldn’t be read because it isn’t in the correct format

发布于 2020-11-28 03:42:53

I'm trying to consume the tmdb api and when I make my request, it tells me that my information is in a bad format.

does anyone know why? I leave my code below.

 import Foundation

struct result:Codable{
 public let page: Int?
  public let totalResults: Int?
 public let totalPages: Int?
 var results:[ModelAPI]?
}

  struct ModelAPI: Codable{

var id:Int?
var adult: Bool?
var backdroppath: String?
var belongstocollection:String?
var budget:Double?
var genres: [genress]?
var homepage:String?
var imdbid:String?
var originallanguage: String?
var originaltitle: String?
var overview:String?
var popularity:Double?
var posterpath:String?
var productioncompanies: [prodCompanies]?
var porductioncountries: [productionCountries]?
var releasedate:String?
var revenue: Int?
var runtime:Int?
var spokenlanguages:String?
var status:String?
var tagline:String?
var title:String?
var video:Bool?
var voteaverage:Int?
var votecount:Int?

enum CodingKeys: String, CodingKey {
    
    case spokenlanguages = "spoken_languages"
    case imdbid = "imdb_id"
    case voteaverage = "vote_average"
    case votecount = "vote_count"
    case productioncompanies = "production_companies"
    case porductioncountries = "porduction_countries"
    case releasedate = "release_date"
    case posterpath = "poster_path"
    case originaltitle = "original_title"
    case originallanguage = "original_language"
    case backdroppath = "backdrop_path"
    case belongstocollection = "belongs_to_collection"
    case genres = "genres"
    case id  = "id"
    case adult = "adult"
    case budget = "budget"
    case homepage = "homepage"
    case overview = "overview"
    case popularity = "popularity"
    case revenue = "revenue"
    case runtime = "runtime"
    case status = "status"
    case tagline = "tagline"
    case title = "title"
    case video = "video"
    
    
}
init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if let id = try container.decode(Int?.self, forKey:.id ) {
        self.id = id
    }
    if let adult = try container.decode(Bool?.self, forKey:.adult ) {
        self.adult = adult
    }
    if let backdroppath = try container.decode(String?.self, forKey:.adult ) {
        self.backdroppath = backdroppath
    }
    if let belongstocollection = try container.decode(String?.self, forKey:.belongstocollection ) {
        self.belongstocollection = belongstocollection
    }
    if let budget = try container.decode(Double?.self, forKey:.budget ) {
        self.budget = budget
    }
    if let genres = try container.decode([genress]?.self, forKey:.genres ) {
        self.genres = genres
    }
    
    if let homepage = try container.decode(String?.self, forKey:.homepage ) {
        self.homepage = homepage
    }
    
    if let imdbid = try container.decode(String?.self, forKey:.imdbid ) {
        self.imdbid = imdbid
    }
    if let originallanguage = try container.decode(String?.self, forKey:.originallanguage )    {
        self.originallanguage = originallanguage
    }
    if let originaltitle = try container.decode(String?.self, forKey:.originaltitle ) {
               self.originaltitle = originaltitle
           }
    if let overview = try container.decode(String?.self, forKey:.overview ) {
                      self.overview = overview
                  }
    
    if let popularity = try container.decode(Double?.self, forKey:.popularity ) {
                      self.popularity = popularity}
    
    if let posterpath = try container.decode(String?.self, forKey:.posterpath ) {
                      self.posterpath = posterpath
                  }
    
    if let productioncompanies = try container.decode([prodCompanies]?.self, forKey:.productioncompanies ) {
                      self.productioncompanies = productioncompanies
                  }
    
    if let porductioncountries = try container.decode([productionCountries]?.self,       forKey:.porductioncountries ) {
                      self.porductioncountries = porductioncountries
                  }
    if let releasedate = try container.decode(String?.self, forKey:.releasedate ) {
                             self.releasedate = releasedate
                         }
    if let revenue = try container.decode(Int?.self, forKey:.revenue ) {
        self.revenue = revenue
    }
    if let runtime = try container.decode(Int?.self, forKey:.runtime ) {
        self.runtime = runtime
    }
    if let spokenlanguages = try container.decode(String?.self, forKey:.spokenlanguages ) {
        self.spokenlanguages = spokenlanguages
    }
    
    if let status = try container.decode(String?.self, forKey:.status ) {
        self.status = status
    }
    if let tagline = try container.decode(String?.self, forKey:.tagline ) {
        self.tagline = tagline
    }
    if let title = try container.decode(String?.self, forKey:.title ) {
        self.title = title
    }
    if let video = try container.decode(Bool?.self, forKey:.video ) {
        self.video = video
    }
    if let voteaverage = try container.decode(Int?.self, forKey:.voteaverage ) {
        self.voteaverage = voteaverage
    }
    if let votecount = try container.decode(Int?.self, forKey:.votecount ) {
               self.votecount = votecount
           }
}

}
  struct languages: Codable{

var englishname:String?
var iso6391:String?
var name:String?

enum CodingKeys: String, CodingKey {
   case englishname = "english_name"
    case iso6391 = "iso_639_1"
    case name = "name"
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if let englishname = try container.decode(String?.self, forKey:.englishname ) {
        self.englishname = englishname
    }
    
    if let iso6391 = try container.decode(String?.self, forKey:.iso6391 ) {
        self.iso6391 = iso6391
    }
    if let name = try container.decode(String?.self, forKey:.name ) {
        self.name = name
    }
}


}
  struct productionCountries:Codable{
  var iso31661:String?
  var name:String?

enum CodingKeys: String, CodingKey {
    case iso31661 = "iso_3166_1"
    case name = "name"
}
init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if let iso31661 = try container.decode(String?.self, forKey:.iso31661 ) {
        self.iso31661 = iso31661
    }
    
    if let name = try container.decode(String?.self, forKey:.name ) {
        self.name = name
    }
}

}
    struct prodCompanies:Codable{
     var id: Int?
      var logopath:String?
    var name:String?
    var origincountry:String?

  enum CodingKeys: String, CodingKey {
    case origincountry = "origin_country"
    case logopath = "logo_path"
    case id = "id"
    case name = "name"
}
init(from decoder: Decoder) throws {
     let container = try decoder.container(keyedBy: CodingKeys.self)
     if let origincountry = try container.decode(String?.self, forKey:.origincountry ) {
         self.origincountry = origincountry
     }
     
     if let name = try container.decode(String?.self, forKey:.name ) {
         self.name = name
     }

    if let id = try container.decode(Int?.self, forKey:.id ) {
        self.id = id
    }
    if let logopath = try container.decode(String?.self, forKey:.logopath ) {
        self.logopath = logopath
      }
      }

   }

          struct genress: Codable {
            var id: Int?
              var name:String?
            enum CodingKeys: String, CodingKey {
           case id = "id"
            case name = "name"
          }
       init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let name = try container.decode(String?.self, forKey:.name ) {
         self.name = name
       }

       if let id = try container.decode(Int?.self, forKey:.id ) {
        self.id = id
         }
    

         }
      }

I did a lot more code because I found that many times jsondecoder does not support the coding keys.

equally below I leave my viewcontroller

class ViewController: UIViewController {

@IBOutlet var movieCollection: UICollectionView!
var delegate:API?
var task = URLSession.shared
var tmdbAPI = "https://api.themoviedb.org/3/movie/upcoming?api_key="
var APIKey = "b64d7f3ead34bfc2d9ade2eb40d81e37"

override func viewDidLoad() {
    super.viewDidLoad()
    movieCollection.delegate = self
    movieCollection.dataSource = self
    getAPI()
}


func getAPI(){
    
    let URLString = "\(tmdbAPI)"+"\(APIKey)" + "&language=en-US"
    let url = URL(string: URLString)!
    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue( "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJiNjRkN2YzZWFkMzRiZmMyZDlhZGUyZWI0MGQ4MWUzNyIsInN1YiI6IjVmYmYyZDUyYTNkMDI3MDA0MGJkNjcyNiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.NLq6xkhLx7uDI6SwyaFrGOcIi3yidJOLK76tmR7a6O8", forHTTPHeaderField: "Authorization")
    
    task.dataTask(with: request) { (data, response, erro) in
         guard let datos = data else {
                       self.delegate?.APIError(error:.endPointError)
                       return
                   }
                   do{
                       let json = try JSONDecoder().decode(result.self, from: datos)
                       debugPrint("json",json)
                   }catch {
                       debugPrint(error.localizedDescription)
                       self.delegate?.APIError(error: .errorResponse)
                   }
    }.resume()
   
}

}

Questioner
Jael Ruvalcaba
Viewed
0
Pratik Prajapati 2020-11-28 12:37:58

The way you making your model is wrong, your're not assigning default value in case of nil or error. So decoding always failed.

try to create model this way or use online tools:

struct ResultRootModel : Codable {
let results : [ResultsModel]?
let page : Int?
let total_results : Int?
let total_pages : Int?

enum CodingKeys: String, CodingKey {

    case results = "results"
    case page = "page"
    case total_results = "total_results"
    case total_pages = "total_pages"
}

init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    results = try values.decodeIfPresent([ResultsModel].self, forKey: .results)
    page = try values.decodeIfPresent(Int.self, forKey: .page)
    total_results = try values.decodeIfPresent(Int.self, forKey: .total_results)
    total_pages = try values.decodeIfPresent(Int.self, forKey: .total_pages)
}

}


struct ResultsModel : Codable {
let video : Bool?
let vote_average : Double?
let popularity : Double?
let vote_count : Int?
let release_date : String?
let adult : Bool?
let backdrop_path : String?
let overview : String?
let genre_ids : [Int]?
let title : String?
let original_language : String?
let original_title : String?
let poster_path : String?
let id : Int?

enum CodingKeys: String, CodingKey {

    case video = "video"
    case vote_average = "vote_average"
    case popularity = "popularity"
    case vote_count = "vote_count"
    case release_date = "release_date"
    case adult = "adult"
    case backdrop_path = "backdrop_path"
    case overview = "overview"
    case genre_ids = "genre_ids"
    case title = "title"
    case original_language = "original_language"
    case original_title = "original_title"
    case poster_path = "poster_path"
    case id = "id"
}

init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    video = try values.decodeIfPresent(Bool.self, forKey: .video)
    vote_average = try values.decodeIfPresent(Double.self, forKey: .vote_average)
    popularity = try values.decodeIfPresent(Double.self, forKey: .popularity)
    vote_count = try values.decodeIfPresent(Int.self, forKey: .vote_count)
    release_date = try values.decodeIfPresent(String.self, forKey: .release_date)
    adult = try values.decodeIfPresent(Bool.self, forKey: .adult)
    backdrop_path = try values.decodeIfPresent(String.self, forKey: .backdrop_path)
    overview = try values.decodeIfPresent(String.self, forKey: .overview)
    genre_ids = try values.decodeIfPresent([Int].self, forKey: .genre_ids)
    title = try values.decodeIfPresent(String.self, forKey: .title)
    original_language = try values.decodeIfPresent(String.self, forKey: .original_language)
    original_title = try values.decodeIfPresent(String.self, forKey: .original_title)
    poster_path = try values.decodeIfPresent(String.self, forKey: .poster_path)
    id = try values.decodeIfPresent(Int.self, forKey: .id)
}

}

change this line

let json = try JSONDecoder().decode(result.self, from: datos) 

to

let json = try JSONDecoder().decode(ResultRootModel.self, from: datos)

Some suggestion:

start Struct/Class name with Uppercase

print print(error) instead debugPrint(error.localizedDescription) it will give more explanation on error like below:

keyNotFound(CodingKeys(stringValue: "budget", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "results", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"budget\", intValue: nil) (\"budget\").", underlyingError: nil))