APIレスポンスをenumにマッピングする際、当てはまるものがなければunknownにしたい時がありました。
愚直に書くと以下のようになります。
enum Enum: String {
case a
case b
case unknown
}
extension Enum {
init(rawValue: RawValue) {
self = Self(rawValue: rawValue) ?? .unknown
}
}
しかし、以下のようなProtocolに準拠すると特定のcaseが存在することを明示的に表すことができます。
protocol HasUnknown, CaseIterable {
static var unknown: Self { get }
}
enum Enum: String, HasUnknown {
case a
case b
case unknown
}
そして、以下のようにprotocol extensionでinitを宣言しておけば、enumごとにマッピング処理を書く必要がなくなります。
extension HasUnknown where Self: RawRepresentable, RawValue: Equatable {
init(rawValue: RawValue) {
self = Self.allCases.first(where: { $0.rawValue == rawValue }) ?? .unknown
}
}
以下のようにinitができます
let a = Enum(rawValue: "a") // case a
let unknown = Enum(rawValue: "others") // case unknown
Revision
2021/05/09 @takehito-koshimizu さんのアドバイスでマッピング処理がシンプルになり、CaseIterableに準拠する必要もなくなりました。ありがとうございます!
2021/05/11 と思いましたが、やはり既存で用意されているrawValueを使ったOptional initはコンパイルエラーは出ないものの、
実行時に無限ループとなりました。若干冗長ではありますがCaseIterableに準拠する必要があります。