Skip to content

Instantly share code, notes, and snippets.

@loromits
Created October 2, 2019 08:21
Show Gist options
  • Save loromits/def360941969ad02b9d15090015e14e1 to your computer and use it in GitHub Desktop.
Save loromits/def360941969ad02b9d15090015e14e1 to your computer and use it in GitHub Desktop.
Dynamic member lookup based simplification to access nested dictionary fields
@dynamicMemberLookup
final class JSONContainer {
private typealias Dict = [String: Any]
private enum Container {
case dict(Dict), value(Any), nothing
}
private let _container: Container
init(_ dict: [String: Any]) {
_container = .dict(dict)
}
init(_ value: Any) {
_container = (value as? Dict).map(Container.dict) ?? .value(value)
}
private init() {
_container = .nothing
}
/// Get either dictionary value by field name, or array value by index
subscript(dynamicMember member: String) -> JSONContainer {
switch _container {
case .dict(let dict):
if let value = dict[member] {
return JSONContainer(value)
}
case .value(let arr as [Any]):
if let index = Int(member), arr.indices.contains(index) {
return JSONContainer(arr[index])
}
default:
break
}
return JSONContainer()
}
/// Cast to type
func `as`<T>(_ type: T.Type) -> T? {
switch _container {
case .value(let value as T), .dict(let value as T):
return value
default:
return nil
}
}
/// Substitute to reciever type
func sub<T>() -> T? {
return `as`(T.self)
}
}
extension Dictionary where Key: CustomStringConvertible {
var json: JSONContainer {
return JSONContainer(self)
}
}
@loromits
Copy link
Author

loromits commented Oct 2, 2019

let fifthNumber = dict.json.phone_numbers.4.number.as(String.self)
let categoryName: String? = dict.json.type.category.name.sub()

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