1

This is a model design related question related to JSONDecoder codeable functionality on swift.

I have the following JSON:

"author": {
    "name": "abc",
    "emailAddress": "[email protected]",
    "id": 8665,
    "displayName": "A B C",
    "active": true,
    "slug": "abc",
    "type": "NORMAL",
    "links": {
        "self": [
                  {
                    "href": "some_href"
                  }
                ]
    }
 }

I am trying to decode this using the new swift 4 Codeable functionality.

Hence I created my struct as follows:

struct Author: Codeable {
    let name: String,
    let emailAddress: String,
    let id: String,
    let displayName: String,
    let active: String,
    let type: String,
    let links: [String: [Link]] 
}

struct Link: Codeable {
    let href: String
} 

Once i run a JSONDecoder().decode on this json i get the model objects in the above format. However, the links property in Author class is obtained as a dictionary. And now to access the value of self field i need to roughly do as follows:

let selfLink = author.links["self"]
let href = selfLink[0].href 

Is there a better way to model the struct so that this can be avoided?

2
  • Thank you @code-different will try this out. Commented May 14, 2018 at 10:50
  • Thank you @jogendar-choudhary. Will try out this approach. Commented May 14, 2018 at 10:51

3 Answers 3

1

Yes, You can do this by adding one more variable like that:

struct Author: Codable {
    let name: String
    let emailAddress: String
    let id: String
    let displayName: String
    let active: String
    let type: String
    let links: [String: [Link]]
    var linkArr: [Link]? {
        return links["self"]
    }
}

And get directly like that:

author.linkArr[0].href
Sign up to request clarification or add additional context in comments.

Comments

1

If you are sure that each author always has at least 1 href, you can add a computed property:

struct Author: Codable {
    let name: String
    let emailAddress: String
    let id: String
    let displayName: String
    let active: String
    let type: String

    let links: [String: [Link]]     
    var href: String {
        return links["self"]!.first!.href
    }
}

You can also make links inaccessible from outside by declaring it private.

Comments

1

One thing that is not very usual or at least named inappropriately in your JSON is that links property is actually not a collection but a wrapper object for a collection self. That's why I would stay true to what the data is and map the links property to name linksWrapper (an object) and self to links (a collection).

struct Author: Codable {
    let name: String
    let emailAddress: String
    let id: Int
    let displayName: String
    let active: Bool
    let slug: String
    let type: String
    let linksWrapper: LinksWrapper

    enum CodingKeys: String, CodingKey {
        case name, emailAddress, id, displayName, active, slug, type
        case linksWrapper = "links"
    }
}

struct LinksWrapper: Codable {
    let links: [Link]

    enum CodingKeys: String, CodingKey {
        case links = "self"
    }
}

struct Link: Codable {
    let href: String
}

Then you can access the links in the same hierarchical order like your JSON model suggests, but with more meaningful names:

let myLinks = author.linksWrapper.links.first

But for your convenience you can add a readonly links property to Author:

var links: [Link] {
    return linksWrapper.links
}

Then you can access links directly:

let myLinks = author.links

However if you have a chance to fix this in the JSON structure itself then do that instead.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.