1
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

【Swift】Optional<Wrapped>型でエラー処理を行う

エラー処理には、Optional<Wrapped>型を用いた処理の他にも、
Result<Success, Failure>型を用いた処理やdo-catch文などもあります。

エラー処理は、私自身よく理解できておらず正直なところ苦手です。

しかし、コードを書く上では絶対に必要な処理なので
備忘録がてら皆さんに共有できたらなと思います。

Optional<Wrapped>型によるエラー処理

ご存知かと思いますがOptional<Wrapped>型は、
値が存在するかどうかを表現することができます。

Optional<Wrapped>型はnilを許容するので、
値が存在する時は格納されている値を、存在しない場合はnilを返します。

エラー処理で使う場合は
値が存在することを成功・存在しないことを失敗とみなせば、
エラー処理の機構として成り立ちますよね、という話です。

これでしたら結構簡単そうです!

実装方法

例えば、エラーが発生し得る関数であれば、
その戻り値をOptional<Wrapped>型にすればエラー処理を行うことができます。

下記のサンプルコードでは、
引数のidに該当するインスタンスがあったらそのインスタンスを返しています。
なかった場合はnilが返ります。


struct User {
    var id: Int
    var name: String
}

// インスタンス化
let user1 = User(id: 1, name: "Tarou")
let user2 = User(id: 2, name: "Zirou")

// インスタンスを配列に格納
var users = [user1, user2]

// エラーが起きる可能性のある関数
// 引数のidに該当するものがなかったらnilを返す
func findUserById(id: Int) -> User? {
    for user in users {
        if user.id == id {
            return user
        }
    }
    return nil
}

// 値が存在する場合
if let user = findUserById(id: 1) {
    print(user.name)
} else {
    print("Error: User not found")
}

// 値が存在しない場合
if let user = findUserById(id: 3) {
    print(user.name)
} else {
    print("Error: User not found")
}

実行結果
Tarou
Error: User not found

Optional<Wrapped>型を処理の成否に利用している例としては、
他にも失敗可能イニシャライザなどがあります。

次のサンプルコードでは、
引数のメールアドレスを二分割できない場合は
不正なデータとみなしてインスタンス化を失敗させています。

let item = mail.split(separator: "@")のitemは、
mailを二分割した時の2つの値が格納された配列です。


struct User {
    var name: String
    var mail: String

    init?(name: String, mail: String) {
        let item = mail.split(separator: "@")
        guard item.count == 2 else {
            return nil
        }
        self.name = name
        self.mail = mail
    }
}

let user1 = User(name: "Tarou", mail: "Tarou@gmail.com")
let user2 = User(name: "Zirou", mail: "Zirou.com")

var users = [user1, user2]

for user in users {
    if let user = user {
        print("Name: \(user.name)\nMail: \(user.mail)")
    } else {
        print("Error: Invalid data")
    }
}

実行結果
Name: Tarou
Mail: Tarou@gmail.com
Error: Invalid data

利用するタイミング

利用するタイミングとしては、値の有無だけで結果を十分に表せる場合です。

先ほどのように、idで一致するものを検索する場合や
メールアドレスの形式がおかしくないかのチェックなどの単純なものは
Optional<Wrapped>型でいいと思います!

ただ、id検索する際に、データベースに接続できなかったり
データが重複していたりなどとエラーの種類を多くすることは恐らく難しいですね・・・。

なので失敗の原因に応じてメッセージを表示したり、
復旧の処理を行ったりするのであればOptional<Wrapped>型では不十分です!

結論として、Optional<Wrapped>型は単純なケースのみにしましょう!

また、冒頭で紹介した、
Result<Success, Failure>型を用いた処理やdo-catch文ですが、
別で記事にする予定なのでぜひご覧ください!

【Swift】Result<Success, Failure>型でエラー処理を行う
【Swift】do-catch文でエラー処理を行う(その1)
【Swift】fatalError関数によるプログラムの終了
【Swift】アサーションによるプログラムの終了

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
1
Help us understand the problem. What are the problem?