【Swift】enum(列挙型)の使い方!値型enumと関連型enumとは

この記事からわかること

  • Swiftenum(列挙型)とは?
  • 定義する方法使い方
  • 値型enum関連値enum特徴違い
  • Bool型などString/Int/Double以外の型を使用するには?
  • RawRepresentableプロトコルとは?
  • CaseIterableプロトコルで全要素を取得する方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

Swiftのenum(列挙型)の使い方やメリットなどを自分の備忘録がてらまとめていきます。

enum(列挙型)とは?

Swiftの列挙型(enumerated type)値を集合として名前をつけて管理することができます。enumを使って定義し、型名は任意の名前を指定することができます。型名の最初の文字は大文字にすることが推奨されています。

enum Language {
    case html
    case css
    case php
    case swift
}

中には複数の要素(メンバー)caseを使って列挙します。各メンバーは下記のように1行にまとめて宣言することも可能です。

enum Language {
    case html,css,php,swift
}

列挙型をswitch文のcaseとして使う

列挙型は値によって処理を分岐させるswitch文と組み合わせて使われることが多いです。

enum Language {
    case html
    case css
    case php
    case swift
}

func printLang(lang: Language) {
        switch lang {

          case .html:
              print( "HELLO! HTML!" )
          case .css:
              print( "HELLO! CSS!" )
          case .php:
              print( "HELLO! PHP!" )
          case .swift:
              print( "HELLO! SWIFT!" )

        }
}

// 変数に列挙型の値を格納
let l = Language.swift
// 格納された列挙型の値により分岐
printLang(lang:l) // HELLO! SWIFT!

各要素に型や値を指定する:値型enum(raw value enum)

列挙型で定義したメンバーには型を指定することも可能です。指定できる型は「String型(文字列)」、「Int型(整数)」、「Double型(浮動小数点)」のみです。それ以外の型を指定した場合はエラーになってしまいます。

enum Language:Double{    
    case html
    case css
    case php
    case swift
}

// エラー
enum Language:Bool{    
    case html
    case css
    case php
    case swift
}

'Language' declares raw type 'Bool', but does not conform to RawRepresentable and conformance could not be synthesized
Raw type 'Bool' is not expressible by a string, integer, or floating-point literal
// 'Language'はraw型'Bool'を宣言しますが、RawRepresentable(※1)に準拠しておらず、準拠を合成できませんでした
// 生の型'Bool'は、文字列、整数、または浮動小数点リテラルでは表現できません

※1:RawRepresentableプロトコルについては後述しています。

また各要素に値を持たせることも可能です。

enum Language:Double {
    case html = 5.0
    case css = 3.0
    case php = 8.1
    case swift = 5.4
}

このように型や値を割り当てた列挙型を「値型enum(raw value enum)」と呼びます。

※rawValueとして渡せるのはリテラル(コンパイル時に値が既に定まっているもの)のみになっています。それ以外を渡そうとするとエラーが発生します。

列挙型のメンバーを参照する

列挙したメンバーにはenumで宣言した型のドットシンタックスで参照できます。格納する変数に型が宣言してあればメンバー名だけでも参照可能になります。メンバー名がわからない場合はfromRaw(n)n番目のメンバーを取得可能です。存在しない番号が指定された場合はnilが返ります。

var lang = Language.html
print(lang) // html
type(of: lang) // Language.Type

var lang2 : Language // enum型名で宣言しておく
lang2 = .html

lang = Language.fromRaw(0)

出力してみると文字列「html」が出力されますが、変数の型を調べると「Language.Type」となります。

列挙型の値を取得する

値型enumの要素に定義された値を取得するにはrawValueを使います。

var langHtml = Language.html
langHtml.rawValue // 5

各要素の値が未定義の場合は整数型の場合は上から0.1.2...の順番に、文字列型の場合は要素名と同名の文字列が自動的に格納されます。

enum Language:Double {
    case html
    case css
    case php
    case swift
}

Language.html.rawValue // 0
Language.css.rawValue // 1
Language.php.rawValue // 2
Language.swift.rawValue // 3

enum Framework:String{
    case Perfect
    case Slimane
    case Swifton
    case Vapor
}

Framework.Perfect.rawValue // Perfect
Framework.Slimane.rawValue // Slimane
Framework.Swifton.rawValue // Swifton
Framework.Vapor.rawValue // Vapor

値から該当する列挙型を取得する

値型enumの場合は指定した値から列挙型を取得することも可能です。その場合はイニシャライザLanguage(rawValue:)を使用します。存在しない場合はnilが返ります。

enum Language:Double {
    case html = 5.0
    case css = 3.0
    case php = 8.1
    case swift = 5.4
}

let type = Language(rawValue: 5.0)
let type2 = Language(rawValue: 5.1)

print(type)  // html
print(type2) // nil

関連型enum: Associated Value

値型enumとは違いメンバーに関連値を紐付ける列挙型関連型enum(Associated Value)と呼びます。値型の場合は列挙している集合自体に型を定義し、各メンバーには既に値が割り振られます。しかし関連型enumでは各メンバーごとに型を定義でき、また値を後からセットすることができます。

enum Guitar {
    case maker(String) // メーカー
    case string(Int)  //  弦数
    case pickup(String)  // ピックアップの種類
}
var guitar = Guitar.maker("Fender")
guitar = . string(6)

使い方とメリットを考えてみる

enumが活躍する時はグループの中に色々な値を定義したい時です。例えば「記事のカテゴリ」というグループの中に「HTML、CSS、PHP、Swift」を定義する場合を考えてみます。

struct Article {
    var title:String
    var category // ここを何で定義する?
    var body:String
}

特定の記事に対してカテゴリを付与する時、「1=HTML、2=CSS」のように数字と対応させるか、そのまま都度「HTML、CSS」と記述する方法になると思います。ですがそれぞれデメリットがあります。

数値の場合

可読性が低い

struct Article {
    var title:String
    var category:Int // 1=HTML,2=CSS,3=PHP,4=Swiftとしておく
    var body:String
}

let data = Article(title: "記事1", category: 1, body: "内容")

数値の場合はそれぞれに対応する表を別途用意(頭の中で)しておき、各値に紐づいた数値を指定することになります。

この場合自分しか識別できず、コメントとして残して置いたとしても可読性が低く、優しくありません。

文字の場合

誤入力が起こる

struct Article {
    var title:String
    var category:String // 文字列で都度指定する
    var body:String
}

let data = Article(title: "記事1", category: "HTML", body: "内容")

文字列の場合は都度、文字列を手打ちしないといけません。「HTML」などを「HMTL」などと誤入力する可能性がある上に、何でも入力出来てしまうのはリスクが大きいです。

列挙型の場合

この両方のデメリットを補ってくれるのが列挙型です。実際に列挙型で定義してみます。

enum Language {
    case HTML
    case CSS
    case PHP
    case Swift
}

struct Article {
    var title:String
    var category:Language
    var body:String
}

let data = Article(title: "記事1", category: .HTML, body: "内容")

先に列挙型Languageを定義しておき、Article構造体のcategoryプロパティの型に指定します。これでcategoryプロパティに指定できるのはLanguageに定義してある項目のみにすることができます。

列挙型を使用するメリット

メリット

可読性が高い

まずは可動性が向上します。選択できる項目は列挙型として明示的に定義してあるので参照もしやすくなります。

コード補完で入力

Swiftのenum(列挙型)を使ってコード補完が起動している様子

列挙型で定義した項目はLanguage.(または型が決まっている状態で.)まで入力すると予測が表示されるので手打ちしなくても簡単に指定することができるようになります。誤入力の可能性が極めて低くなるのは大きなメリットだと思います。

switch文でのエラー

expression failed to parse:
error: switch must be exhaustive

Swiftのenum(列挙型)を使ってswitch文を使用の際に発生しているエラー

switch文を使った分岐処理を記述する際に列挙型を比較値として指定する場合、各項目を網羅できるように記述しないとエラーを起こしてくれます。これにより分岐処理の記述漏れを起こすことがなくなります。

switch data.category {
    case .HTML:
        print("HTMLだよ")
    case .CSS:
        print("CSSだよ")
    case .PHP:
        print("PHPだよ")
    case .Swift:
        print("Swiftだよ")
}

RawRepresentableプロトコル

RawRepresentableプロトコルはSwiftに元々定義されているプロトコルで、enumのcaseに値を持たせることができるプロトコルです。列挙型では元々「String型(文字列)」、「Int型(整数)」、「Double型(浮動小数点)」しか定義できませんRawRepresentableプロトコルを明示的に付与することでそれら以外の型の値を持たせることができるようになります。

また「String型(文字列)」、「Int型(整数)」、「Double型(浮動小数点)」はRawRepresentableプロトコルがSwiftコンパイラが自動で付与してくれているのでrawValueで値を取得できるようになっています。

列挙型の全要素を取得する:CaseIterable

CaseIterableプロトコルはSwift 4.2から使用可能になったプロトコルで定義されているプロパティの値をコレクション形式で取得できるallCasesプロパティが実装されます。

enum Language:String,CaseIterable {
    case HTML
    case CSS
    case PHP
    case Swift
}

print(Language.allCases)
// [__lldb_expr_1.Language.HTML, __lldb_expr_1.Language.CSS, __lldb_expr_1.Language.PHP, __lldb_expr_1.Language.Swift]

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index