SwiftのDecodableなenumで将来的な値追加に対応する
Swift4から追加されたDecodableは, APIのレスポンスモデルを定義する際にとても便利な機能です.
しかし, 将来的に値が増える可能性のあるenumの場合には注意が必要です.
import Foundation
let decoder = JSONDecoder()
enum Enum: String, Decodable, CaseIterable {
case a
case b
}
print(try decoder.decode([Enum].self, from: """
["a", "b", "c"]
""".data(using: .utf8)!))
例えば上記のコードを実行すると, Cannot initialize Enum from invalid String value c となって例外が投げられます.
しかし, type
のようなフィールドの場合, サポートされていない物だけを後でフィルタしたいといったことは往々にして存在します.
そんな時の対応方法です.
init?(rawValue:)
を実装する
import Foundation
let decoder = JSONDecoder()
enum Enum: String, Decodable, CaseIterable {
case a
case b
case unspecified
init?(rawValue: String) {
self = type(of: self).allCases.first { $0.rawValue == rawValue } ?? .unspecified
}
}
print(try decoder.decode([Enum].self, from: """
["a", "b", "c"]
""".data(using: .utf8)!))
この方法はCaseIterable
を用いて対応していないrawValue
の時にfallbackするようなinit?(rawValue:)
を実装することで実現します.
decode以外の場合にも影響する点には注意が必要ですが, シンプルで分かりやすい方法ではないでしょうか.
init(from decoder:) throws
を実装する
import Foundation
let decoder = JSONDecoder()
enum Enum: String, Decodable {
case a
case b
case unspecified
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(RawValue.self)
self = type(of: self).init(rawValue: rawValue) ?? .unspecified
}
}
print(try decoder.decode([Enum].self, from: """
["a", "b", "c"]
""".data(using: .utf8)!))
この場合だとinit?(rawValue:)
自体はデフォルトのままなので, Enum(rawValue: "c")
とやった場合はnil
になりますが, Decoder
を使った場合は.unspecified
になります.
正しく実装するのであれば, こちらでしょうか.
enumを要素に持つstructで対応する
import Foundation
let decoder = JSONDecoder()
struct Res: Decodable {
enum Enum: String, Decodable {
case a
case b
case unspecified
}
private enum CodingKeys: String, CodingKey {
case typeRaw="type"
}
var type: Enum {
return Enum(rawValue: self.typeRaw) ?? .unspecified
}
private let typeRaw: Enum.RawValue
}
print((try decoder.decode(Res.self, from: """
{"type": "c"}
""".data(using: .utf8)!)).type)
あまり綺麗ではありませんが, レスポンスによってfallbackの有無を分けたい場合に使えるかもしれません.
こんなところでしょうか.
記事が気に入ったらチップを送ることができます!
You can give me a cup of coffee :)
Kyash ID: soranoba
Amazon: Wish List
GitHub Sponsor: github.com/sponsors/soranoba
PayPal.Me: paypal.me/soranoba
(Updated: )