0

I wrote an extension to Decodable with the hopes of having a generic constructor for objects from json strings, it looks like this:

extension Decodable {
    init?(with dictionary: [String: Any]) {
        guard let data = try? JSONSerialization.data(
            withJSONObject: dictionary,
            options: .prettyPrinted
        ) else {
            return nil
        }
        guard let result = try? JSONDecoder().decode(
            Self.self,
            from: data
        ) else {
            return nil
        }
        self = result
    }
}

An example use case looks like this:

guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return }
guard var goer = Goer(with: json) else { return }

And the object I'm trying to decode into looks like this:

struct Goer: Codable {
    let goerId: String
    let created: Double
    let username: String
    let firstName: String
    let lastName: String
    let city: String
    let bio: String
    let isPrivate: Bool
    let numFollowers: Int
    let numFollowing: Int
    let followers: [GoerFollow]
    let following: [GoerFollow]
}

My issue is that I want to introduce some optionals to these objects that the json strings I'm trying to decode may or may not have keys for. This generic constructor fails in the case where there is no key:value in the json for an optional variable in the object.

I've seen that I can write a custom constructor with a decoder for each object and use the decodeIfPresent function but I wonder if there is a way to do it generically.

Sebby Fay
  • 181
  • 1
  • 5
  • 3
    Maybe I'm missing something, but it seems that you're doing some unnecessary steps here -- why use `JSONSerialization.jsonObject` to take `Data` and turn it into a `Dictionary`, which you then pass to `JSONSerialization.data` to turn it *back into* `Data`, which you then decode with `JSONDecoder`. Why not just use `JSONDecoder` on the original `Data`? – jnpdx Mar 09 '22 at 02:36
  • _” there is no key:value in the json”_, that is quite different than only the value being nil and I think you must have a custom init for this situation – Joakim Danielson Mar 09 '22 at 07:20
  • It’s a bit pointless to write a generic init for decoding, if the json suits the generic init then the synthesized init you get for free for your custom types will also work so you might as well use that instead. – Joakim Danielson Mar 09 '22 at 07:59

1 Answers1

0
if let jsonData = data {

    do {
        var model = try decoder.decode(Goer.self, from: jsonData)
        print("model:\(model)")
    } catch {
        print("error:\(error)")
    }
}


struct Goer: Codable {
    let goerId: String?
    let created: Double?
    let username: String?
    let firstName: String?
    let lastName: String?
    let city: String?
    let bio: String?
    let isPrivate: Bool?
    let numFollowers: Int?
    let numFollowing: Int?
    let followers: [GoerFollow]?
    let following: [GoerFollow]?
}
Leo Chen
  • 327
  • 1
  • 8