Decode the included part from JSON API using the Codable protocol

Viewed 560

the API which I am trying to consume in the iOS application which I am developing is the JSON API. I was using a library in order to parse the data which was being retrieved by the server. Now I am trying to parse the JSON on my own using the new Codable protocol which Swift introduced.

I successfully managed to parse the data and the attributes part but the difficulty I am facing is on the included part.

To start with I created this class:

class UserCodable: Codable {

var data: UserCodableData?
var relationships: UserRelationships?

    enum CodingKeys: String, CodingKey {

        case data
        case relationships = "included"

    }

}

in order to store the data retrieved for the User object.

Along with this class, I created these two structures

struct UserCodableData: Codable {

    var id: String?
    var type: String?
    var attributes: UserAttributes?

}

struct UserAttributes: Codable {

    var id: String?
    var type: String?
    var firstName: String?
    var lastName: String?
    var email: String?
    var officePhone: String?
    var mobilePhone: String?
    var timeZone: String?
    var active: Int?
    var middleName: String?
    var `extension`: String?
    var homePhone: String?
    var avatar: String?

    enum CodingKeys: String, CodingKey {

        case id
        case type
        case firstName = "firstname"
        case lastName = "lastname"
        case email
        case officePhone = "office_phone"
        case mobilePhone = "mobile_phone"
        case timeZone = "time_zone"
        case active
        case middleName = "middlename"
        case `extension`
        case homePhone = "home_phone"
        case avatar

    }

}

in order to store the data and the attributes appropriately.

Finally I created the structure regarding the relationships (included):

struct UserRelationships: Codable {

    var role: RoleCodable?

}

The RoleCodable class follows the same pattern.

The data retrieved regarding the included key is the following:

"data": {

     },
"included": [
    {
        "id": "10",
        "type": "roles",
        "attributes": {
            "title": "Role"
        }
    }
],

The problem is that the included part contains an array of JSON objects. How can I decode and initialise the object in the UserRelationships class - in this case the role of type RoleCodable?

1 Answers

This is not elegant but it is how I am doing it, and it involves a manual init(from decoder: Decoder) function for UserDecodable. Basically I read in the 'included' section twice: the first time to gather type information and the second time to construct the relationships with the correct types.

I am using the key .included for "included" rather than .relationships since "relationships" occurs as another section within a JSON API response and might cause confusion.

Here's the container for type information that you can re-use for any included relationships:

struct ResourceIdentifierObject: Codable {
    let type: String
    let id: String

    enum CodingKeys: String, CodingKey {
        case type
        case id
    }
}

And this is the first read:

let resourceIds = try data.decode([ResourceIdentifierObject].self,
                                  forKey: .included)

Second time around we iterate through the 'included' JSON array whilst checking the type in our ResourceIdentifierObject at the same index and build each relationship manually (I've added a comments relationship to demonstrate different relationships):

var included = try data.nestedUnkeyedContainer(forKey: .included)

while !included.isAtEnd {
    let resourceId = resourceIds[included.currentIndex]
    switch resourceId.type {
    case "roles":
        role = try included.decode(RoleCodable.self)
    case "comments":
        comments.append(try included.decode(CommentCodable.self))
    default:
        print("unexpected type")
    }
}
Related