Generic decoding json into object with optionals

375 Views Asked by At

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.

1

There are 1 best solutions below

0
On
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]?
}