Skip to content

Instantly share code, notes, and snippets.

Embed
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
}
}
@imario42

This comment has been minimized.

Copy link

imario42 commented Nov 5, 2017

Thanks for your gist!
Just looked at the encoding part for now, and found that this is a good addition so the dictionary can hold and serialize any Encodable.

        case let v as Encodable: try v.encode(to: self.superEncoder(forKey: dynamicKey))
@rezakhmf

This comment has been minimized.

Copy link

rezakhmf commented Jun 4, 2019

thanks for this awesome gist, any example?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.