Last active
August 3, 2017 16:18
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Updated:
DictionaryValueType
protocol so types such as enums (who areAny
rather thanAnyObject
can specify output and be handled automatically)