I am trying to parse a JSON object that I have as a result from my network session (done through MoyaProvider). The returned JSON object has nested JSON arrays in it. It looks something like this:
Edit: Link to the json file is here. results.json
{
“resultCount” : 50,
“results” :
[
{
“data1”: 1
“data2”: 2
},
{
“data1”: 1
“data2”: 2
},
{
“data1”: 1
“data2”: 2
}
]
}
Now using Moya, I could get the data by using Moya.Response API like this:
let jsonObj = try response.mapJSON()
But I don't want to do that I want to map it to my Model struct. I have done something like this below. Which I have checked (through OPTION + MouseHover) that objMovie is of type [Movie]
let objMovie = try response.map(ITunesSearchResults<Movie>.self).results
I have followed a tutorial online using similar technique but I d don't understand why objMovie does not contain the return values from after executing the line above. I tried to do a
print(obj.< propertyofMovie >)
but nothing is showing on the console.
So what gives?
Here are some code snippets. Where ITunesSearchResults is:
struct ITunesSearchResults<T: Decodable>: Decodable {
let results: [T]
}
And my Movie structure is this. It conforms to the key values found in the JSON nested array properties.
struct Movie: Codable
{
let trackId: Int
let trackName: String
let trackGenre: String
let trackPrice: Int?
let longDescription: String
init(trackId: Int, trackName: String, trackGenre: String,
trackPrice:
Int?, /*trackImage: Thumbnail,*/ longDescription: String)
{
self.trackId = trackId
self.trackName = trackName
self.trackGenre = trackGenre
self.trackPrice = trackPrice ?? 0
self.longDescription = longDescription
//self.trackImage = trackImage //TODO: thumbnail: mapp url from json
}
private enum MovieCodingKeys: String, CodingKey
{
case trackId
case trackName
case trackGenre = "primaryGenreName"
case trackPrice
//case trackImage
case longDescription
}
init(from decoder: Decoder) throws
{
let container = try decoder.container(keyedBy: MovieCodingKeys.self)
trackId = try container.decode(Int.self, forKey: .trackId)
trackName = try container.decode(String.self, forKey: .trackName)
trackGenre = try container.decode(String.self, forKey: .trackGenre)
trackPrice = try container.decode(Int?.self, forKey: .trackPrice)
longDescription = try container.decode(String.self, forKey: .longDescription)
}
}
First of all use the Data
response of Moya
as you do want to decode the JSON with Decodable
for example
let data = response.data
Your Movie
struct is too complicated, explicit CodingKeys
and an init
method is not needed at all, you get them for free. This is sufficient:
struct ITunesSearchResults<T: Decodable>: Decodable {
let results: [T]
}
struct Movie: Decodable
{
let trackId: Int
let trackName: String
let trackGenre: String?
let trackPrice: Double?
let longDescription: String
}
Note that trackPrice
is Double
and trackGenre
and trackPrice
are optional.
Now decode simply
do {
let result = try JSONDecoder().decode(ITunesSearchResults<Movie>.self, from: data)
print(result)
} catch { print(error) }
Note:
Never use a syntax like
try container.decode(Int?.self, forKey: .trackPrice)
There is decodeIfPresent
try container.decodeIfPresent(Int.self, forKey: .trackPrice)
The huge benefit is it throws an error if the key is present but the type is wrong like in this concrete case.