【Swift】enum(列挙型)の使い方!値型enumと関連型enumとは
この記事からわかること
- Swiftのenum(列挙型)とは?
- 定義する方法と使い方
- 値型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
に定義してある項目のみにすることができます。
列挙型を使用するメリット
メリット
- 可読性が高い
- コード補完で入力できる
- switch文で網羅しないとエラーが出る
可読性が高い
まずは可動性が向上します。選択できる項目は列挙型として明示的に定義してあるので参照もしやすくなります。
コード補完で入力
列挙型で定義した項目はLanguage.
(または型が決まっている状態で.
)まで入力すると予測が表示されるので手打ちしなくても簡単に指定することができるようになります。誤入力の可能性が極めて低くなるのは大きなメリットだと思います。
switch文でのエラー
expression failed to parse:
error: switch must be exhaustive
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]
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。