2

I want to create a struct that will contain a dictionary of encodable items, and itself be encodable. This is because I don't know the exact data structure, it will depend on connected database. I tried to do something like this:

struct UserInfo: Encodable {
    let personalInformation: [String : Encodable]
}

But it gives an error, that UserInfo does not conform to Encodable. I think it should, because it will only contain encodable items. Can this be achieved somehow easily?

Damian Dudycz
  • 2,622
  • 19
  • 38
  • 3
    This is essentially the problem of [protocols not conforming to themselves](https://stackoverflow.com/questions/42561685/why-cant-a-get-only-property-requirement-in-a-protocol-be-satisfied-by-a-proper/42716340#42716340). – Dávid Pásztor Feb 14 '18 at 10:51

1 Answers1

1

I think I have found a nice enough solution to my problem.

public enum JSONValue: Encodable {

    case string(String)
    case int(Int)
    case double(Double)
    case dict([String : JSONValue])
    case array([JSONValue])
    case bool(Bool)
    case null

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .int   (let x): try? container.encode(x)
        case .double(let x): try? container.encode(x)
        case .string(let x): try? container.encode(x)
        case .dict  (let x): try? container.encode(x)
        case .array (let x): try? container.encode(x)
        case .bool  (let x): try? container.encode(x)
        case .null:          try? container.encodeNil()
        }
    }

}

struct Test: Encodable {
    let name: String
    let dict: [String : JSONValue]
}

This way I can create object like this:

let t = Test(name: "Damian", dict: ["string" : JSONValue.string("asd"), "number" : JSONValue.int(127)])
let data = try! JSONEncoder().encode(t)

print(String(data: data, encoding: .utf8)!)

and get

{"name":"Damian","dict":{"number":127,"string":"asd"}}
Damian Dudycz
  • 2,622
  • 19
  • 38