Last active
September 29, 2016 02:16
-
-
Save rhysforyou/1633eacaf5084bcecf7ce5cc9c81f84c to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import Foundation | |
import Decodable | |
struct Restaurant { | |
enum Meal: String { | |
case breakfast, lunch, dinner | |
} | |
enum DecodingError: Error { | |
case missing(String) | |
case invalid(String, Any) | |
} | |
fileprivate static var dateFormatter: ISO8601DateFormatter = { | |
let dateFormatter = ISO8601DateFormatter() | |
dateFormatter.formatOptions = [.withInternetDateTime] | |
return dateFormatter | |
}() | |
static let mockData: [String: Any] = [ | |
"name": "Mock Restaurant", | |
"coordinates": [ | |
"lat": -33.8684299, | |
"lng": 151.19378410000002 | |
], | |
"meals": [ | |
"breakfast", | |
"lunch", | |
"dinner" | |
], | |
"opening": "1970-01-01T08:00:00Z", | |
"closing": "1970-01-01T22:00:00Z" | |
] | |
let name: String | |
let location: (latitude: Double, longitude: Double) | |
let meals: Set<Meal> | |
let openingTime: Date | |
let closingTime: Date | |
} | |
// Vanilla decoding (0.9s for 10k records) | |
extension Restaurant { | |
init(json: [String : Any]) throws { | |
guard let name = json["name"] as? String else { | |
throw DecodingError.missing("name") | |
} | |
guard let coordinatesJSON = json["coordinates"] as? [String: Double], | |
let latitude = coordinatesJSON["lat"], | |
let longitude = coordinatesJSON["lng"] else { | |
throw DecodingError.missing("coordinates") | |
} | |
guard let mealsJSON = json["meals"] as? [String] else { | |
throw DecodingError.missing("meals") | |
} | |
guard let openingTimeString = json["opening"] as? String else { | |
throw DecodingError.missing("opening") | |
} | |
guard let closingTimeString = json["closing"] as? String else { | |
throw DecodingError.missing("closing") | |
} | |
guard let openingTime = Restaurant.dateFormatter.date(from: openingTimeString) else { | |
throw DecodingError.invalid("opening", openingTimeString) | |
} | |
guard let closingTime = Restaurant.dateFormatter.date(from: closingTimeString) else { | |
throw DecodingError.invalid("closing", closingTimeString) | |
} | |
var meals: Set<Meal> = [] | |
for string in mealsJSON { | |
guard let meal = Meal(rawValue: string) else { | |
throw DecodingError.invalid("meals", string) | |
} | |
meals.insert(meal) | |
} | |
self.name = name | |
self.location = (latitude, longitude) | |
self.meals = meals | |
self.openingTime = openingTime | |
self.closingTime = closingTime | |
} | |
} | |
// Decoding with Decodable (1.2s for 10k records) | |
extension Restaurant: Decodable { | |
private static func time(_ value: String, name: String) throws -> Date { | |
guard let date = dateFormatter.date(from: value) else { | |
throw DecodingError.invalid(name, value) | |
} | |
return date | |
} | |
static func decode(_ json: Any) throws -> Restaurant { | |
let meals = try Array.decode(json => "meals") | |
.flatMap(Meal.init) | |
return try Restaurant(name: json => "name", | |
location: (json => "coordinates" => "lat", json => "coordinates" => "lng"), | |
meals: Set(meals), | |
openingTime: time(json => "opening", name: "opening"), | |
closingTime: time(json => "closing", name: "closing")) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment