I need to parse a JSON generated by iTunes Search API. I'm looking for all apps by a certain developer, and I usually get a response like this (simplified):
{
"resultCount":4,
"results": [
{
"wrapperType":"artist",
"artistType":"Software Artist",
"artistName":"Vladimir Kelin"
},
{
"wrapperType":"software",
"formattedPrice":"Free",
"trackId":933241656
},
{
"wrapperType":"software",
"formattedPrice":"$1.99",
"trackId":1175807581
}
]
}
As you can see, the results
array is heterogenous, meaning it contains objects of 2 different types: artist
and software
.
I created a structure, implementing a new handy Codable
protocol:
struct App: Codable {
let trackId: Int
let formattedPrice: String?
/// - Parameter jsonDict: A dictionary produced by JSONSerialization
init?(jsonDict: [String: Any]) {
guard let trackId = jsonDict["trackId"] as? Int else { return nil }
self.trackId = trackId
formattedPrice = jsonDict["formattedPrice"] as? String
}
}
I don't want to parse artist
data, so I didn't created any structures for that.
It's relatively easy to parse such JSON using JSONSerialization
:
do {
let root = try unwrap(JSONSerialization.jsonObject(with: receivedData, options: []) as? [String: Any])
let results = try unwrap(root["results"] as? [[String: Any]])
var apps = [App]()
for result in results {
// Check "software" or "artist"
guard result["wrapperType"] as? String == "software" else { continue }
guard let app = App(jsonDict: result) else { continue }
apps.append(app)
}
completion(.ok, apps)
} catch let e {
let dataStr = String(bytes: receivedData, encoding: .utf8) ?? ""
print("Exception: \"\(e)\", while parsing data: \"\(dataStr)\"")
completion(.parseError, nil)
}
Do you see the unwrap()
function for the first time?
The Problem. I'm looking for a way to parse the response using wonderful JSONDecoder
class introduced in Swift 4. If the results
array were homogenous I would be able to parse it in just few strings of code.
// Helper struct
struct TunesResponse: Codable {
let resultCount: Int
let results: [App]
}
// Parsing
let jsonDecoder = JSONDecoder()
if let parsedResponse = jsonDecoder.decode(TunesResponse.self, from: receivedData) {
let apps = parsedResponse.results
completion(.ok, apps)
} else {
completion(.parseError, nil)
}
In practice I spend a lot of time but didn't found any way to apply JSONDecoder for such kind of response.
Artist
,App
) returned depending on thewrapperType
value \$\endgroup\$