ObjecTips

Swift & Objective-C で iOS とか macOS とか

CaseIterable を使って case の index を取得する

前段

Swift 4.2 の CaseIterableallCaeses を使って enum の case の一覧や個数が取得可能になり随分便利になった。
表題の enum の index の取得について、まず Int 型の場合は以下の様に CaseIterable を使わなくても rawValue を使ってシンプルに取得出来る。

enum Animal: Int {
    case dog
    case cat
    case rabbit
}

let index: Int = Animal.cat.rawValue

CaseIterable を使って case の index を取得する

では Int 型の enum が様々な事情により以下の様に定義されている場合どうだろう。

enum Animal: Int {
    case dog = 1
    case cat = 3
    case rabbit = 4
}

この場合 CaseIterable を適用すれば下記の様に allCases のコレクションを使って index の取得が出来る。
firstIndex(of:) が返す型は Optional の Int? 型だが .cat は確実に allCases に存在しているので Optional を unwrap している。

enum Animal: Int, CaseIterable {
    case dog = 1
    case cat = 3
    case rabbit = 4
}

let index: Int = Animal.allCases.firstIndex(of: .cat)!

rawValue に依存しないで index が取得出来る様になったので型も Int 型に縛られる事がなくなる。
例えば String だったり

enum Animal: String, CaseIterable {
    case dog = "イヌ"
    case cat = "ネコ"
    case rabbit = "うさぎ"
}

let index: Int = Animal.allCases.firstIndex(of: .cat)!

型自体を外してしまって RawRepresentable プロトコルに適合しなくしても良い。

enum Animal: CaseIterable {
    case dog
    case cat
    case rabbit
}

let index: Int = Animal.allCases.firstIndex(of: .cat)!

Extension にする

色々汎用化を試みて CaseIterable の Extension とする実装に落ち着いた。
これを Swift のプロジェクトに入れておけば CaseIterable な enum から一発で index を取得する事が出来る。

extension Hashable where Self : CaseIterable {
    var index: Self.AllCases.Index {
        return type(of: self).allCases.firstIndex(of: self)!
    }
}

以下の様に簡単に書けるので UITableView 周りで section と row から IndexPath を作る時などに便利に使ってる。

enum Animal: CaseIterable {
    case dog
    case cat
    case rabbit
}

let index: Int = Animal.cat.index

*1


ソースファイル

https://gist.github.com/Koze/59a1b31c45217b9f46c11737ba905534#file-caseiterable-index-swift

index of CaseIterable enum

*1:毎回 allCases を呼ぶためリソースの無駄がある事は頭の隅に置いておくべきかも。それをシビアに気にしなくちゃいけない様な巨大な enum はあまり無いとは思うけど。