【Swift】enum でIF文によるcase判定やassociated valueの取り出し方、enum同士を比較(==)する方法について【備忘録】

Swift でアプリ開発をしていると、enum は個人的に必須といえるほど頻繁に使用しています。

Left Caption

ねじまき

enum なくして私のコーディングライフは存在し得ない...!

まだまだ駆け出しの身ですが、enum と associated value を覚えてからは多用しまくっています。

久しぶりにコードを書く時とかに「あれ?こういう時どうするんだっけ?」ということがないように、enum についての備忘録を残しておきます。

基本的な enum の定義

ここでは例として、API の通信結果を表す enum を定義してみます。

//
enum APIResultType{
    // 成功時の型(レスポンスデータ)
    case Success(data: String)
    // 失敗時の型(エラー時のステータスコードとエラーメッセージ)
    case Error(statusCode: Int, errorMessage: String)
}

API成功時には、その関連値(associated value)にレスポンスデータを持たせています。

失敗時は、ステータスコード(404など)と、エラーメッセージを関連値に持たせています。

enum の case を判別して関連値を取り出す

以下のような enum 変数があるとします。

let result = APIResultType.Success(data: "レスポンスデータです")

この result から関連値(data)を取り出すためには、基本的にはswitch文を使用します。

//
switch result {
case let .Success(data: data):
    print(data)
case let .Error(statusCode: statusCode, errorMessage: errorMessage):
    print(statusCode, errorMessage)
}

宗教上の理由でswitchを使いたくない

そんな人がいるかは分かりませんが...(笑)

でももう少し簡潔に書きたい時ってありますよね。

例えば、目的の case が .Success の時だけだったりすると、Switch 構文は書くのが面倒だったりするかもしれません。

そんな時は、if文を使用して任意のcaseの時だけ処理することができます。

//
if case let .Success(data: data) = result{
    print(data)
}

個人的には、caseを網羅する必要がない時はif文を使用することが多いです。

enum 同士をif文でイコール判定したい

enumが関連値を持たない場合は普通に==演算子を使用してイコール判定することができます。

//
enum SampleType{
    case One
    case Two
}

let one = SampleType.One
let two = SampleType.Two

if one == two{
    // 何らかの処理...
}else{
    // 何らかの処理...
}

しかし、enumが関連値を持つ場合(先ほどのAPIResultTypeのような場合)、上記のような書き方をするとエラーになってしまいます。

//

let success = APIResultType.Success(data: "レスポンスデータです")
let error = APIResultType.Error(statusCode: 404, errorMessage: "エラーです")

if success == error{
    print("同じ型です")
}

// -> エラーメッセージ  
// Binary operator '==' cannot be applied to two 'APIResultType' operands 

関連値を持つenum同士を==で比較したい場合は、enum を Equatableプロトコルに準拠させる必要があります。

//
extension APIResultType: Equatable{
    static func ==(lhs: APIResultType, rhs: APIResultType) -> Bool{
        switch (lhs, rhs) {
        case (let .Success(lhsData), let .Success(rhsData)):
            return true
        case (let .Error(statusCode: lhsStatusCode, errorMessage: lhsErrorMessage),
              let .Error(statusCode: rhsStatusCode, errorMessage: rhsErrorMessage)):
            return true
        default:
            return false
        }
    }
}

これで、enum同士を比較することができるようになりました。

上の例は、関連値が同じかどうかまでは見ていません。厳密に関連値まで一致するかを比較する場合はコードを追加する必要がありますが、個人的にその用途はあまりないかなあと思っています。(分かりませんが・・)