-
-
Save khanlou/bc90b27155f5c7c137d9697c083cda89 to your computer and use it in GitHub Desktop.
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
struct ParserError: ErrorType { | |
let message: String | |
} | |
struct Parser { | |
let dictionary: [String: AnyObject]? | |
init(dictionary: [String: AnyObject]?) { | |
self.dictionary = dictionary | |
} | |
func fetch<T>(key: String) throws -> T { | |
let fetchedOptional = dictionary?[key] | |
guard let fetched = fetchedOptional else { | |
throw ParserError(message: "The key \"\(key)\" was not found.") | |
} | |
guard let typed = fetched as? T else { | |
throw ParserError(message: "The key \"\(key)\" was not the right type. It had value \"\(fetched).\"") | |
} | |
return typed | |
} | |
func fetchOptional<T>(key: String) throws -> T? { | |
let fetchedOptional = dictionary?[key] | |
guard let fetched = fetchedOptional else { | |
return nil | |
} | |
guard let typed = fetched as? T else { | |
throw ParserError(message: "The key \"\(key)\" was not the right type. It had value \"\(fetched).\"") | |
} | |
return typed | |
} | |
func fetch<T, U>(key: String, transformation: T -> U?) throws -> U { | |
let fetched: T = try fetch(key) | |
guard let transformed = transformation(fetched) else { | |
throw ParserError(message: "The value \"\(fetched)\" at key \"\(key)\" could not be transformed.") | |
} | |
return transformed | |
} | |
func fetchOptional<T, U>(key: String, transformation: T -> U?) -> U? { | |
return (dictionary?[key] as? T).flatMap(transformation) | |
} | |
func fetchArray<T, U>(key: String, transformation: T -> U?) throws -> [U] { | |
let fetched: [T] = try fetch(key) | |
return fetched.flatMap(transformation) | |
} | |
} | |
enum SomeEnum: String { | |
case FirstCase = "first_case" | |
case SecondCase = "second_case" | |
} | |
struct InnerType { | |
let aString: String | |
let anInt: Int | |
let stringArray: [String] | |
let anOptionalValue: String? | |
init?(dictionary: [String: AnyObject]?) { | |
let parser = Parser(dictionary: dictionary) | |
do { | |
self.aString = try parser.fetch("a_string") | |
self.anInt = try parser.fetch("an_int") | |
self.stringArray = try parser.fetch("string_array") | |
self.anOptionalValue = try parser.fetchOptional("bingo") | |
} catch let error { | |
print(error) | |
return nil | |
} | |
} | |
} | |
class OuterType { | |
let inner: InnerType | |
let enums: [SomeEnum] | |
init?(dictionary: [String: AnyObject]?) { | |
let parser = Parser(dictionary: dictionary) | |
do { | |
self.inner = try parser.fetch("inner") { InnerType(dictionary: $0) } | |
self.enums = try parser.fetchArray("enums") { SomeEnum(rawValue: $0) } | |
} catch let error { | |
print(error) | |
return nil | |
} | |
} | |
} | |
var innerDictionary = [String: AnyObject]() | |
innerDictionary["a_string"] = "some_string" as? AnyObject | |
innerDictionary["an_int"] = 3 as? AnyObject | |
innerDictionary["string_array"] = ["a", "bunch", "of", "strings"] as? AnyObject | |
InnerType(dictionary: innerDictionary) | |
var outerDictionary = [String: AnyObject]() | |
outerDictionary["inner"] = innerDictionary as? AnyObject | |
outerDictionary["enums"] = ["first_case", "second_case"] as? AnyObject | |
let outer = OuterType(dictionary: outerDictionary) | |
outer?.inner.stringArray | |
outer?.enums |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I really like your approach it is clean straight forward and probably faster than 3rd party libraries.
I came across one thing though, if you have an API returning "null" for optional values you can not separately type check in fetchOptional.
Because the guard let checking for the existence will not fail and return, so it will always throw an error if the optional value is not there.
This only works if the API does not return that field at all, like you simulate with:
self.anOptionalValue = try parser.fetchOptional("bingo")
One can prevent that by type checking right away like this:
I was not able to find another way right now. If you can think of anything let me know I would greatly appreciate it.