I'm fairly new to the swift programming language and have been trying to get this to work for the last week or so. I'm working with an existing API that returns JSON data whose structure changes a little depending on how many venues get returned.
The real structure is somewhat more complicated, but this example illustrates the problem. In one flavor of result, I get a single venue returned like:
{
"totalItems": 21,
"pageSize": 2,
"venues": {
"venue":
{
"name": "Venue Name 1"
"location": "Location A",
"showCount": "4"
}
}
}
In another flavor of result, I get an array of venues returned:
{
"totalItems": 21,
"pageSize": 2,
"venues": {
"venue":
[{
"name": "Venue Name 1"
"location": "Location A",
"showCount": "4"
},
{
"name": "Venue Name 2"
"location": "Location B",
"showCount": "2"
}]
}
}
Yes - the owner of this API should have returned an array regardless, but they didn't and it cannot be changed.
I was able to get this to decode properly for an array of venues (or even if no venues were passed), but it aborts when a single venue is passed (due to the structure variation of course). My code also worked when I changed it to accommodate a single venue, but then returns of multiple venues aborted.
What I'd like to do is decode either variation into an internal structure containing an array regardless of which variation I receive, thus making things far simpler for me to deal with programmatically afterward. Something like this:
struct Response: Decodable {
let totalItems: Int
let pageSize: Int
let venues: VenueWrapper
struct VenueWrapper: Decodable {
let venue: [Venue] // This might contain 0, 1, or more than one venues
}
struct Venue: Decodable {
let name: String
let location: String
let showCount: Int
}
}
Note: In the actual JSON response, there are actually several substructures like this in the response (e.g., a single structure vs an array of structures) which is why I felt simply creating an alternate structure was not a good solution.
I'm hoping someone has come across this before. Thanks in advance!
You can create your own decoder,
struct Response: Decodable {
let totalItems: Int
let pageSize: Int
let venues: VenueWrapper
struct VenueWrapper: Decodable {
var venue: [Venue]
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
venue = []
if let singleVenue = try? values.decode(Venue.self, forKey: CodingKeys.venue) {
//if a single venue decoded append it to array
venue.append(singleVenue)
} else if let multiVenue = try? values.decode([Venue].self, forKey: CodingKeys.venue) {
//if a multi venue decoded, set it as venue
venue = multiVenue
}
enum CodingKeys: String, CodingKey { case venue }
}
}
struct Venue: Decodable {
let name: String
let location: String
let showCount: String
}
}