🍁

Swift: CaseIterableな列挙型をGenericsで使う

2022/12/10に公開

SwiftUIで列挙型(enum)を利用する際、.allCasesForEach() {}に突っ込みたくなることがあります。そしてさらにそのViewを複数の列挙型で使いまわしたいときもあるでしょう。そんなときは以下のようにGenericsで渡せばOKです。

enumの例
enum TimeZone: String, CaseIterable {
    case morning
    case afternoon
    case evening
    case night
}

enum Season: String, CaseIterable {
    case spring
    case summer
    case autumn
    case winter
}
genericsの例
func enumItemsPicker<E: RawRepresentable & Hashable & CaseIterable>(
    selection: Binding<E>
) -> some View where E.RawValue == String, E.AllCases == Array<E> {
    Picker(selection: selection) {
        ForEach(E.allCases, id: \.rawValue) { item in
            Text(item.rawValue).tag(item)
        }
    } label: {
        EmptyView()
    }
    .pickerStyle(.menu)
}

ポイント

  • プレースホルダの型に複数プロトコルを定義するときは&でつなぐ
    • 今回の要件の場合はRawRepresentable & Hashable & CaseIterable
  • .rawValueを使いたいので、where句でプレースホルダの型.RawValue == 型のように定義する
  • .allCasesを使いたいので、where句でプレースホルダの型.AllCases == Array<プレースホルダの型>のように定義する
利用例
struct Hoge: View {
    @State var timeZone = TimeZone.morning
    @State var season = Season.spring
    
    var body: some View {
        VStack {
            enumItemsPicker(selection: $timeZone)
            enumItemsPicker(selection: $season)
        }
    }
    
    func enumItemsPicker...
}

Discussion