【Swift】列挙型（enum）について

# 目次
[列挙型とは](#列挙型とは)
[定義方法](#定義方法)
[ローバリュー](#ローバリュー)
[連想値](#連想値)
[CaseIterableプロトコル](#caseiterableプロトコル)

# 列挙型とは
列挙型は値型の一種で、**複数の識別子をまとめる型**です。例えば季節には春夏秋冬の4種類がありますが、列挙型ではこれら4つの識別子をまとめて1つの方として扱えます。列挙型の一つ一つの識別子はケースと言います。また、春であると同時に夏であることがありえないように、ケースどうしは排他的です。

# 定義方法
列挙型の定義にはenumキーワードとcaseキーワードを使用します。
またインスタンス化は、列挙型名.ケース名のようにケース名を指定して行います。

```swift
enum Season {
   case spring
   case summer
   case autumn
   case winter
 }

let spring = Season.spring  // spring
let summer = Season.summer  // summer
```

クラスや構造体のようにイニシャライザを定義してインスタンス化を行うこともできます。
次の例では、イニシャライザinit(japaneseName:)を追加し、引数に渡された文字列に応じて各ケースをselfに代入しています。
```swift
enum Season {
   case spring
   case summer
   case autumn
   case winter

      init?(japaneseName: String) {
          switch japaneseName {
          case "春": self = .spring
          case "夏": self = .summer
          case "秋": self = .autumn
          case "冬": self = .winter
          default: return nil  // 春夏秋冬以外の場合はnilを返す
          }
      }
 }

let april = Season(japaneseName: "春")!  // spring
let august = Season(japaneseName: "夏")! // summer
```
# ローバリュー
列挙型のケースには**それぞれに対応する値を設定できます**。この値を**ローバリュー**(raw value)と言い、すべてのケースのローバリューの型は同じである必要があります。また、ローバリューの型に指定できるのは、数値型、String型、Character型などのリテラルに変更可能な型になります。
ローバリューを定義するには、型名の後に：とローバリューの型を記述します。

次の例では、列挙型SymbolにCharacter型のローバリュー"#"、"$"、"%"を設定します。

```swift
enum Symbol: Character {
    case sharp = "#"
    case dollar = "$"
    case percent = "%"
}
```
ローバリューが定義されている列挙型では、**ローバリューと列挙型の相互変換を行うための失敗可能イニシャライライザinit?(reawValue:)とプロパティrawValueが暗黙的に用意されます**。init?(rawValue:)は設定したローバリューと同じ型の値を引数に取り、ローバリューが一致するケースがあればそのケースをインスタンス化し、なければnilを返します。また、rawValueプロパティは、ケースのローバリューを返します。

```swift
enum Symbol: Character {
    case sharp = "#"
    case dollar = "$"
    case percent = "%"
}

let symbol1 = Symbol(rawValue: "#")  // sharp
let character1 = symbol?.rawValue    // "#"

let symbol2 = Symbol(rawValue: "&")  // nil
let character2 = symbol2?.rawValue   // nil
```

### ローバリューのデフォルト値
**Int型やString型ではローバリューにデフォルト値が存在し、特に値を指定しない場合はデフォルトが使用されます**。
Int型のローバリューのデフォルト値は、最初のケースが0で、それ以降は前のケースに１を足した値となります。また、String型のローバリューの値は、ケース名をそのまま文字列にした値となります。

```swift
enum Symbol : Int {
    case sharp
    case dollar
    case percent
}

Symbol.sharp.rawValue   // 0
Symbol.dollar.rawValue  // 1
Symbol.percent.rawValue // 2
```
```swift
enum Symbol : String {
    case sharp
    case dollar
    case percent
}

Symbol.sharp.rawValue   // "sharp"
Symbol.dollar.rawValue  // "dollar"
Symbol.percent.rawValue // "percent"
```



# 連想値
列挙型のインスタンスは、どのケースかということに加えて、連想値(associated value)という**付加情報**を持つこともできます。連想値に指定できる型には制限がありません。
例えば、色の代表的な数値表現にRGB(Red,Green,Blue)とCMYK(Cyan,Magenta,Yellow,Key)がありますが、表現方法をケース、数値を連想地として表現すれば、これらを列挙型として表現できます。

```swift
enum Color {
    case rgb(Float, Float, Float)
    case cmyk(Float, Float, Float, Float)
}

let rgb = Color.rgb(0.0, 0.33, 0.66)
let cmyk = Color.cmyk(0.0, 0.33, 0.66, 0.99)

let color = rgb

switch color {
case .rgb(let r, let g, let b):
    print("rgb: \(r),\(g),\(b)")
case .cmyk(let c, let m, let y, let k):
    print("cmyk: \(c),\(m),\(y), \(k)")
}

// 実行結果　　rgb: 0.0,0.33,0.66
```

# CaseIterableプロトコル
### allCasesプロパティ
列挙型を使用していると、すべてのケースを配列として取得したい場合があります。
そこでCaseIterableプロトコルへの準拠を宣言した列挙型には自動的にallCasesスタティックプロパティが追加され、このプロパティが**列挙型のすべての要素を返します**。
```swift
enum Season: CaseIterable {
    case spring
    case summer
    case autumn
    case winter
}

Season.allCases  // [spring, summer, autumn, winter]
```

### allCasesプロパティのコードが自動生成されない条件
通常、プロトコルに準拠するためには、プロトコルが定義しているプロパティやメソッドを実装する必要があります。しかし、上記のSeason型の例では、定義しいないにも関わらずallCasesプロパティが使用できていました。これは、**連想値を持たない列挙型がCaseiterableプロトコルへの準拠を宣言した場合、その実装コードがコンパイラによって自動生成されるため**です。

したがって、列挙型が連想値を持つ場合は、以下のようにallCasesプロパティを実装する必要があります。
```swift
enum Fruit: CaseIterable {
    case peach
    case apple(color: AppleColor)
    case orange

    static var allCases: [Fruit] {
        return [
            .peach,
            .apple(color: .red),
            .apple(color: .green),
            .orange
        ]
    }
}

enum AppleColor {
    case red, green
}

Fruit.allCases  // [peach, {red}, {green}, orange]
```


# 参考
・Swift実践入門 (https://gihyo.jp/book/2020/978-4-297-11213-4)