Skip to content

Instantly share code, notes, and snippets.

@tomquist
Last active May 9, 2016 02:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomquist/2f53fcc6d98c70f0a79a120e8a9203f2 to your computer and use it in GitHub Desktop.
Save tomquist/2f53fcc6d98c70f0a79a120e8a9203f2 to your computer and use it in GitHub Desktop.
Decoding heterogeneous array in swift
import Foundation
struct User {
let name: String
let age: Int
}
struct Car {
let color: String
}
enum Entry {
case user(User)
case car(Car)
}
typealias Json = Any
typealias JsonDict = [String: Json]
protocol Decodable {
init(json: Json) throws
}
enum JsonMappingError: ErrorType {
case WrongType
case MissingField
case UnknownType
}
extension User: Decodable {
init(json: Json) throws {
guard let dict = json as? JsonDict else {
throw JsonMappingError.WrongType
}
guard let name = dict["name"] as? String,
age = dict["age"] as? Int else {
throw JsonMappingError.MissingField
}
self.name = name
self.age = age
}
}
extension Car: Decodable {
init(json: Json) throws {
guard let dict = json as? JsonDict else {
throw JsonMappingError.WrongType
}
guard let color = dict["color"] as? String else {
throw JsonMappingError.MissingField
}
self.color = color
}
}
protocol TypedDecodable: Decodable {
init(type: String, dict: JsonDict) throws
}
extension TypedDecodable {
init(json: Json) throws {
guard let dict = json as? JsonDict else {
throw JsonMappingError.WrongType
}
guard let type = dict["type"] as? String else {
throw JsonMappingError.MissingField
}
try self.init(type: type, dict: dict)
}
}
protocol EnumTypedDecodable: TypedDecodable {
static var typeMap: [String: (Json throws -> Self)] { get }
}
extension EnumTypedDecodable {
init(type: String, dict: JsonDict) throws {
guard let creator = Self.typeMap[type] else {
throw JsonMappingError.UnknownType
}
self = try creator(dict)
}
}
extension Entry: EnumTypedDecodable {
static var typeMap: [String: (Json throws -> Entry)] = [
"user": { .user(try User(json: $0)) },
"car": { .car(try Car(json: $0)) },
]
}
let json: [JsonDict] = [
["type": "car", "color": "blue"],
["type": "user", "name": "John", "age": 30]
]
try json.map(Entry.init)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment