How to encode a dictionary of unknown/variable keys?

68 views Asked by At

Say I have to send this data to the server:

struct Event: Codable {
    let title: String
    let params: [String:Any]? //Not allowed
}

So for instance, these events could look like any of these:

let event = Event(title: "cat was pet", params:["age": 7.2])
let event = Event(title: "cat purred", params:["time": 9])
let event = Event(title: "cat drank", params:["water": "salty", "amount": 3.3])

I need the ability to send any arbitrary amount of key/value pairs as params to the event. Is this even possible with Codable? If not, how would I encode params as a json string?

2

There are 2 answers

2
vadian On BEST ANSWER

Codable is magic if all types conform to Codable, but in your case I suggest traditional JSONSerialization. Add a computed property dictionaryRepresentation

struct Event: {
    let title: String
    let params: [String:Any]

    var dictionaryRepresentation: [String:Any] {
        return ["title":title,"params":params] 
    }
}

then encode the event

let data = try JSONSerialization.data(withJSONObject: event.dictionaryRepresentation)

This is less expensive/cumbersome than forcing Any to become Codable (no offense, Rob).

1
Rob Napier On

Using a type like JSONValue, it would look almost identical to what you describe:

import JSONValue

struct Event: Codable {
    let title: String
    let params: JSONValue?  // JSONValue rather than [String: Any]
}

// All the rest is the same
let event1 = Event(title: "cat was pet", params:["age": 7.2])
let event2 = Event(title: "cat purred", params:["time": 9])
let event3 = Event(title: "cat drank", params:["water": "salty", "amount": 3.3])

There's a lot of helper code in JSONValue, but at its heart is just an enum, as described in Swift/JSONEncoder: Encoding class containing a nested raw JSON object literal:

public enum JSONValue {
    case string(String)
    case number(digits: String)
    case bool(Bool)
    case object([String: JSONValue])
    case array([JSONValue])
    case null
}

Everything else is just helpers to encode/decode, conform to ExpressibleBy... protocols, etc.