今回はEnumの意外に知られていない性質についてご紹介していきます。
enumのcaseはprotocolで定義することができる
実はcaseはprotocolで定義することができます。これはSwift5.3で追加された機能です。
このようにして定義します。
protocol EnumProtocol {
static var foo: Self { get }
static func hoge(arg: Int) -> Self
static func fuga(arg1: Int, arg2: String) -> Self
}
enum Hoge: EnumProtocol {
case foo
case hoge(arg: Int)
case fuga(arg1: Int, arg2: String)
}
associated valueがないcaseは、
static var foo: Self { get }
associated valueがあるcaseは、
static func hoge(arg: Int) -> Self
このように、caseをprotocolで定義することができました。
enumはクロージャで表せる
enumのcaseはクロージャで表すことができます。
ただし、クロージャで表せるのはassociated valueを使っているcaseのみとなります。
let foo: () -> Hoge = Hoge.foo // ❌
let hoge: (Int) -> Hoge = Hoge.hoge // 🟢
let fuga: (Int, String) -> Hoge = Hoge.fuga // 🟢
正直、どこでつかうねんという感じですが、
この性質を利用して作られた、swift-case-pathsというライブラリがあります。
詳しい説明は省きますが、KeyPathと同じ感じで、このようなことができるようになったりするライブラリです。
let hoges: [Hoge] = [.foo, .hoge(arg: 2), .fuga(arg1: 1, arg2: 2)]
hoges.map(/Hoge.hoge)
// [.hoge(arg2: 2)]
swift-case-pathsの/
オペレーターの定義はこのようになっています。
public prefix func / <Root, Value>(
embed: @escaping (Value) -> Root
) -> CasePath<Root, Value> {
.init(embed: embed, extract: extractHelp(embed))
}
(Value) -> Root
でcaseをクロージャで受け取っていることがわかると思います。
正直内部実装はあまり理解していないので解説はできないのですが、こういうところで使われているんだなー程度に思っていてください。
まとめ
protocolでenumのcaseを定義できるのは便利ですね。
ただ、closureで定義できる性質は使い道が思い浮かんでいないので、面白い使い方を見つけたいですね。