I am trying to use Swift 4 Decodable to parse an array that contains two different types of objects. The data looks something like this, with the included
array being the one that contains both Member
and ImageMedium
objects:
{
"data": [{
"id": "8f7cbbac-c133-4b5e-a2ec-1f32353018fa",
"type": "post",
"title": "Test Post 1",
"owner-id": "8986563c-438c-4d77-8115-9e5de2b6e477",
"owner-type": "member"
}, {
"id": "f6b3c640-a58b-449f-93c7-f6cb7b569a9c",
"type": "post",
"title": "Test Post 2",
"owner-id": "38d845a4-db66-48b9-9c15-d857166e255e",
"owner-type": "member"
}],
"included": [{
"id": "8986563c-438c-4d77-8115-9e5de2b6e477",
"type": "member",
"first-name": "John",
"last-name": "Smith"
}, {
"id": "d7218ca1-de53-4832-bb8f-dbceb6747e98",
"type": "image-medium",
"asset-url": "https://faketest.com/fake-test-1.png",
"owner-id": "f6b3c640-a58b-449f-93c7-f6cb7b569a9c",
"owner-type": "post"
}, {
"id": "c59b8c72-13fc-44fd-8ef9-4b0f8fa486a0",
"type": "image-medium",
"asset-url": "https://faketest.com/fake-test-2.png",
"owner-id": "8f7cbbac-c133-4b5e-a2ec-1f32353018fa",
"owner-type": "post"
}, {
"id": "38d845a4-db66-48b9-9c15-d857166e255e",
"type": "member",
"first-name": "Jack",
"last-name": "Doe"
}]
}
I have tried a bunch of different ways to solve this cleanly using Decodable, but so far the only thing that has worked for me is making one struct for Included
that contains all the properties of both objects as optionals, like this:
struct Root: Decodable {
let data: [Post]?
let included: [Included]?
}
struct Post: Decodable {
let id: String?
let type: String?
let title: String?
let ownerId: String?
let ownerType: String?
enum CodingKeys: String, CodingKey {
case id
case type
case title
case ownerId = "owner-id"
case ownerType = "owner-type"
}
}
struct Included: Decodable {
let id: String?
let type: String?
let assetUrl: String?
let ownerId: String?
let ownerType: String?
let firstName: String?
let lastName: String?
enum CodingKeys: String, CodingKey {
case id
case type
case assetUrl = "asset-url"
case ownerId = "owner-id"
case ownerType = "owner-type"
case firstName = "first-name"
case lastName = "last-name"
}
}
This can work by implementing a method to create Member
and ImageMedium
objects from the Included
struct based off what its type
property is, however it's obviously less than ideal. I'm hoping there is a way to accomplish this using a custom init(from decoder: Decoder)
, but I haven't gotten it to work yet. Any ideas?
I figured out how to decode the mixed included
array into two arrays of one type each. Using two Decodable structs is easier to deal with, and more versatile, than having one struct to cover multiple types of data.
This is what my final solution looks like for anyone who's interested:
struct Root: Decodable {
let data: [Post]?
let members: [Member]
let images: [ImageMedium]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
data = try container.decode([Post].self, forKey: .data)
var includedArray = try container.nestedUnkeyedContainer(forKey: .included)
var membersArray: [Member] = []
var imagesArray: [ImageMedium] = []
while !includedArray.isAtEnd {
do {
if let member = try? includedArray.decode(Member.self) {
membersArray.append(member)
}
else if let image = try? includedArray.decode(ImageMedium.self) {
imagesArray.append(image)
}
}
}
members = membersArray
images = imagesArray
}
enum CodingKeys: String, CodingKey {
case data
case included
}
}
struct Post: Decodable {
let id: String?
let type: String?
let title: String?
let ownerId: String?
let ownerType: String?
enum CodingKeys: String, CodingKey {
case id
case type
case title
case ownerId = "owner-id"
case ownerType = "owner-type"
}
}
struct Member: Decodable {
let id: String?
let type: String?
let firstName: String?
let lastName: String?
enum CodingKeys: String, CodingKey {
case id
case type
case firstName = "first-name"
case lastName = "last-name"
}
}
struct ImageMedium: Decodable {
let id: String?
let type: String?
let assetUrl: String?
let ownerId: String?
let ownerType: String?
enum CodingKeys: String, CodingKey {
case id
case type
case assetUrl = "asset-url"
case ownerId = "owner-id"
case ownerType = "owner-type"
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With