Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
A CodingKey that is dynamic -- it can be any string! Encode/decode with a Dictionary of `[String : Any]` in the model.
/**
```
// Encode a model with properties of type [String : Any]
var propertiesContainer = container.nestedContainer(keyedBy: DynamicKey.self, forKey: .properties)
if let properties = properties {
try propertiesContainer.encodeDynamicKeyValues(withDictionary: properties)
}
```
*/
struct DynamicKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
}
extension KeyedEncodingContainer where Key == DynamicKey {
mutating func encodeDynamicKeyValues(withDictionary dictionary: [String : Any]) throws {
for (key, value) in dictionary {
let dynamicKey = DynamicKey(stringValue: key)!
// Following won't work:
// let v = value as Encodable
// try propertiesContainer.encode(v, forKey: dynamicKey)
// Therefore require explicitly casting to the supported value type:
switch value {
case let v as String: try encode(v, forKey: dynamicKey)
case let v as Int: try encode(v, forKey: dynamicKey)
case let v as Double: try encode(v, forKey: dynamicKey)
case let v as Float: try encode(v, forKey: dynamicKey)
case let v as Bool: try encode(v, forKey: dynamicKey)
default: print("Type \(type(of: value)) not supported")
}
}
}
}
extension KeyedDecodingContainer where Key == DynamicKey {
func decodeDynamicKeyValues() -> [String : Any] {
var dict = [String : Any]()
for key in allKeys {
// Once again, following decode doesn't work, therefore requires explicitly decoding each supported type.
// propertiesContainer.decode(?, forKey: key)
if let v = try? decode(String.self, forKey: key) {
dict[key.stringValue] = v
} else if let v = try? decode(Bool.self, forKey: key) {
dict[key.stringValue] = v
} else if let v = try? decode(Int.self, forKey: key) {
dict[key.stringValue] = v
} else if let v = try? decode(Double.self, forKey: key) {
dict[key.stringValue] = v
} else if let v = try? decode(Float.self, forKey: key) {
dict[key.stringValue] = v
} else {
print("Key \(key.stringValue) type not supported")
}
}
return dict
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment