Last active
July 18, 2020 09:39
-
-
Save takasek/d805b0e1a0bef7746172fc609995ed88 to your computer and use it in GitHub Desktop.
「派生型プロパティを Decodable で扱う」 https://hitorigoto.zumuya.com/200716_decodableProtocolProperty にインスパイヤされたコード。いじってたらPropertyWrapperがなくなってしまった…
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 CustomCodingKey: CodingKey, ExpressibleByStringLiteral { | |
let stringValue: String | |
let intValue: Int? = nil | |
init?(stringValue: String) { self.stringValue = stringValue } | |
init?(intValue: Int) { return nil } | |
init(stringLiteral value: String) { stringValue = value } | |
init(_ value: String) { stringValue = value } | |
} | |
protocol PolymorphicDecodable: Decodable { | |
static var hintKeyName: String { get } | |
init?(hint: String, decoder: Decoder, container: KeyedDecodingContainer<CustomCodingKey>) throws | |
} | |
extension PolymorphicDecodable { | |
static var hintKeyName: String { "type" } | |
init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CustomCodingKey.self) | |
let hintKey = CustomCodingKey(Self.hintKeyName) | |
let hint = try container.decode(String.self, forKey: hintKey) | |
guard let v = try Self(hint: hint, decoder: decoder, container: container) else { | |
throw DecodingError.dataCorruptedError( | |
forKey: hintKey, | |
in: container, | |
debugDescription: "unknown type hint: \(hint)" | |
) | |
} | |
self = v | |
} | |
} | |
// 以下 ドメイン固有コード | |
struct BoolParameter: Decodable { | |
let defaultValue: Bool | |
} | |
struct NumberParameter: Decodable { | |
let defaultValue: CGFloat | |
let minValue: CGFloat | |
let maxValue: CGFloat | |
} | |
enum BoolOrNumberParameter: PolymorphicDecodable { | |
case bool(BoolParameter) | |
case number(NumberParameter) | |
init?(hint: String, decoder: Decoder, container: KeyedDecodingContainer<CustomCodingKey>) throws { | |
switch hint { | |
case "bool": self = .bool(try BoolParameter(from: decoder)) | |
case "number": self = .number(try NumberParameter(from: decoder)) | |
default: return nil | |
} | |
} | |
} | |
struct Bird: Decodable { | |
let eggs: [String] | |
let destination: URL | |
} | |
enum Animal: PolymorphicDecodable { | |
case dog(name: String) | |
case cat(name: String, cuteFactor: Double) | |
case bird(Bird) | |
static var hintKeyName: String { "kind" } | |
init?(hint: String, decoder: Decoder, container: KeyedDecodingContainer<CustomCodingKey>) throws { | |
switch hint { | |
case "dog": self = .dog(name: try container.decode(String.self, forKey: "name")) | |
case "cat": self = .cat(name: try container.decode(String.self, forKey: "name"), | |
cuteFactor: try container.decode(Double.self, forKey: "cute_factor")) | |
case "bird": self = .bird(try Bird(from: decoder)) | |
default: return nil | |
} | |
} | |
} | |
struct Root: Decodable { | |
let parameter: BoolOrNumberParameter | |
let pets: [Animal] | |
} | |
let root = try JSONDecoder().decode(Root.self, from: """ | |
{ | |
"parameter": { | |
"type": "number", | |
"defaultValue": 1.5, | |
"minValue": 0.0, | |
"maxValue": 2.0 | |
}, | |
"pets": [ | |
{ "kind": "dog", "name": "pochi" }, | |
{ "kind": "cat", "name": "tama", "cute_factor": 99.99 }, | |
{ "kind": "bird", "eggs": [ "uzura", "温泉", "ホビロン" ], "destination": "https://www.youtube.com/watch?v=8kQZHYbZkLs" } | |
] | |
} | |
""".data(using: .utf8)!) | |
dump(root) | |
//▿ __lldb_expr_89.Root | |
//▿ parameter: __lldb_expr_89.BoolOrNumberParameter.number | |
// ▿ number: __lldb_expr_89.NumberParameter | |
// - defaultValue: 1.5 | |
// - minValue: 0.0 | |
// - maxValue: 2.0 | |
//▿ pets: 3 elements | |
// ▿ __lldb_expr_89.Animal.dog | |
// ▿ dog: (1 element) | |
// - name: "pochi" | |
// ▿ __lldb_expr_89.Animal.cat | |
// ▿ cat: (2 elements) | |
// - name: "tama" | |
// - cuteFactor: 99.99 | |
// ▿ __lldb_expr_89.Animal.bird | |
// ▿ bird: __lldb_expr_89.Bird | |
// ▿ eggs: 3 elements | |
// - "uzura" | |
// - "温泉" | |
// - "ホビロン" | |
// ▿ destination: https://www.youtube.com/watch?v=8kQZHYbZkLs | |
// - _url: https://www.youtube.com/watch?v=8kQZHYbZkLs #0 | |
// - super: NSObject |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
※ なおJSONはあくまで例であり、
ではなく
のように、オブジェクト種別のヒントと具体的なデータはnestさせてオブジェクトごと切り分けることを強く勧めます