Skip to content

Instantly share code, notes, and snippets.

@rhysforyou
Last active September 29, 2016 02:16
Show Gist options
  • Save rhysforyou/1633eacaf5084bcecf7ce5cc9c81f84c to your computer and use it in GitHub Desktop.
Save rhysforyou/1633eacaf5084bcecf7ce5cc9c81f84c to your computer and use it in GitHub Desktop.
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