Skip to content

Instantly share code, notes, and snippets.

@IanKeen
Last active August 3, 2017 16:18
Show Gist options
  • Save IanKeen/3a6c3b9a42aaf9fea982 to your computer and use it in GitHub Desktop.
Save IanKeen/3a6c3b9a42aaf9fea982 to your computer and use it in GitHub Desktop.
Protocol to handle conversions between Models (class or struct based) and Dictionaries. Model to Dictionary has a default implementation provided for simple 1:1 Models
protocol DictionaryConvertable {
static func fromDictionary(dictionary: [String: AnyObject]) throws -> Self
func toDictionary() -> [String: AnyObject]
}
protocol DictionaryValueType {
func dictionaryValue() -> AnyObject?
}
extension Optional: DictionaryValueType {
func dictionaryValue() -> AnyObject? {
switch self {
case .Some(let value):
if let value = value as? DictionaryValueType {
return value.dictionaryValue()
}
return value as? AnyObject
case .None: return nil
}
}
}
extension DictionaryConvertable {
func toDictionary() -> [String: AnyObject] {
return convertToDictionary(self)
}
}
func convertToDictionary<T: DictionaryConvertable>(convertable: T) -> [String: AnyObject] {
return Mirror(reflecting: convertable).toDictionary()
}
extension Mirror {
private func toDictionary() -> [String: AnyObject] {
let output = self.children.reduce([:]) { (result: [String: AnyObject], child) in
guard let key = child.label else { return result }
let childMirror = Mirror(reflecting: child.value)
if let style = childMirror.displayStyle where style == .Collection {
let converted: [AnyObject] = childMirror.children
.filter {
$0.value is DictionaryConvertable ||
$0.value is DictionaryValueType ||
$0.value is AnyObject
}
.flatMap { collectionChild in
if let convertable = collectionChild.value as? DictionaryConvertable {
return convertable.toDictionary()
} else if let value = collectionChild.value as? DictionaryValueType {
return value.dictionaryValue()
} else {
return collectionChild.value as? AnyObject
}
}
return result + [key: converted]
} else {
if let value = child.value as? DictionaryConvertable {
return result + [key: value.toDictionary()]
} else if let dictionaryValue = child.value as? DictionaryValueType, let value = dictionaryValue.dictionaryValue() {
return result + [key: value]
} else if let value = child.value as? AnyObject {
return result + [key: value]
}
}
return result
}
if let superClassMirror = self.superclassMirror() {
return output + superClassMirror.toDictionary()
}
return output
}
}
func + <K>(left: [K: AnyObject], right: [K: AnyObject]) -> [K: AnyObject] {
var result = [K: AnyObject]()
[left, right].forEach { dict in
dict.forEach { key, value in
if
let currentValue = result[key] as? [K: AnyObject],
let incomingValue = value as? [K: AnyObject],
let newValue = currentValue + incomingValue as? AnyObject {
result[key] = newValue
} else {
result[key] = value
}
}
}
return result
}
@IanKeen
Copy link
Author

IanKeen commented Dec 11, 2015

Updated:

  • Supports class based models that use inheritance
  • Handles optional properties correctly
  • Adds DictionaryValueType protocol so types such as enums (who are Any rather than AnyObject can specify output and be handled automatically)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment