Skip to content

Instantly share code, notes, and snippets.

@samwize
Created September 28, 2017 06:59
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save samwize/a82f29a1fb34091cd61fc06934568f82 to your computer and use it in GitHub Desktop.
Save samwize/a82f29a1fb34091cd61fc06934568f82 to your computer and use it in GitHub Desktop.
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
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
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