Swift実践入門読書勉強会 #7を開催しました

Swift実践入門読書勉強会 #7をOrigami株式会社で行いました。

https://read-swift.connpass.com/event/56756/

2月から開始したこの勉強会も、今回で最後になりました。

Swift実践入門読書勉強会工藤さん田中さん玉城さん、私佐藤で運営とファシリテーターをしながら、Swift実践入門を一章ずつ読んていく勉強会です。

最後ということで、なんと著者の石川さんもいらっしゃっいました。

テーマは13章の「エラー処理」です。

石川さんも参加!

ファシリテーターは田中さん!

Optional型によるエラー処理

書き方

処理の結果をOptional型で表し、値が存在すれば成功、存在しなければ失敗とみなす。

struct User {  
    let id: Int
    let name: String
    let email: String
}

func findUser(byID id: Int) -> User? {  
    let users = [
        User(id: 1,
             name: "Yusei Nishiyama",
             email: "nishiyama@example.com"),
        User(id: 2,
             name: "Yosuke Ishikawa",
             email: "ishikawa@example.com"),
    ]

    for user in users {
        if user.id == id {
            return user
        }
    }

    return nil
}

let id = 0  
if let user = findUser(byID: id) {  
    print("Name: \(user.name)")
} else {
    print("Error: User not found")
}

使い所

エラーの内容を問わないときに使える方法です。
勉強会内で石川さん直々にお話していただいたことは、例えばサーバーからのjsonファイルからenumのインスタンスを初期化する時。
失敗したとすれば、jsonはenumの構造のキーを持っていなかったということ。そういう時は、失敗したかどうかを見ればいいのでOptional型によるエラー処理が有効とのこと。

勉強会の議論


Result型によるエラー処理

成功したらT型、失敗したらError型が返る様な方を定義するやり方。 Swiftの言語で実装されたものではないが、デファクトスタンダード。

HaskelやRustに同様なエラー処理があり、それをSwiftに適応したものとのこと。
ライブラリーにもなっている。

https://github.com/antitypical/Result

書き方

enum Result<T, Error> {  
    case success(T)
    case failure(Error)
}

enum DatabaseError:Error {  
    case entryNotFound
    case duplicatedEntry
    case invalidEntry(reason: String)
}

struct User {  
    let id: Int
    let name: String
    let email: String
}

func findUser(byID id: Int) -> Result<User, DatabaseError> {  
    let users = [
        User(id: 1,
             name: "Yusei Nishiyama",
             email: "nishiyama@example.com"),
        User(id: 2,
             name: "Yosuke Ishikawa",
             email: "ishikawa@example.com"),
    ]

    for user in users {
        if user.id == id {
            return .success(user)
        }
    }

    return .failure(.entryNotFound)
}

let id = 0  
let result = findUser(byID: id)

switch result {  
case let .success(name):  
    print(".success: \(name)")
case let .failure(error):  
    switch error {
    case .entryNotFound:
        print(".failure: .entryNotFound")
    case .duplicatedEntry:
        print(".failure: .duplicatedEntry")
    case .invalidEntry(let reason):
        print(".failure: .invalidEntry(\(reason))")
    }
}

使い所

Result型の特徴は、エラーの内容がわかり、それが網羅されていること。

以下のときに使うと良い

  • エラーの網羅性がコンパイルで保証する
  • 何が起きるのかが開発者が分かっている
    • do-chatchだと何が起こるのかを自分で調べなきゃいけなくなる。網羅性があまりない
    • 逆に不明なエラーがある場合はunknownなどのエラーを作り、そこに放り込む
  • 非同期処理で使える
    • do-chatch文は同期処理で使える。

勉強会での議論

私が石川さんに質問

「〇〇ページのように、switch文がネストしてしまうことがある。これどうにかならないのか。こうするしかないのか」 すると石川さんは

Result型でswitch文をネストしないようにするコードを書く石川さん

書かれたコードはこんな感じ

Result<User, ~~Error>  
let r1 = findUser(byID: 123)  
let r2 = r1.flatMap{ localRart (fromEmail: $0.email)  
//r2はResult<String, ~~Error)になる

元ネタはSwiftでResult<T, Error>型のライブラリーantitypical/Resultの以下のコード

https://github.com/antitypical/Result/blob/master/Result/ResultProtocol.swift

Result<T, Error>型にflatMapメソッドを定義し、サクセスした時はその値を含めた値に変換する方法を取ればswitch文をネストしなくても良くなるとのことでした。

ただし、このときのErrorは同じ型でないといけないそうです。

do-catch文によるエラー処理

書き方

throw文によるエラーが発生する可能性のある処理をdo節で記述し、catch節にエラー処理を記述します。

enum SomeError: Error {  
    case error1
    case error2(reason: String)
}

do {  
    throw SomeError.error2(reason: "何かがおかしいようです")
} catch SomeError.error1 {
    print("error1")
} catch SomeError.error2(let reason) {
    print("error2: \(reason)")
} catch {
    print("Unknown error: \(error)")
}

使い所

do-catch文の特徴は必ずcatch文を書かなければいけないことがSwiftの言語仕様上決まっていることです。
また、catchできるエラーはResult型と異なり型情報がなくなっています。 (Errorプロトコルを実装した何かということだけしかわからない)

このことから、do-catch文は「どんなエラーが来るか実装時には分からないが、必ず復帰させる処理をしたいとき」にdo-catch文は有効だという話になりました。

勉強会での議論

rethrowsについて

勉強会の議論

deferについて

勉強会の議論

おわりに

7回に渡り、最後まで続けられて本当に嬉しく思います。
この勉強会のいいところは、Swiftの文法書き方をただ覚えるのではなく、「これはなぜそうなっているのか」「どういう場面で使われるのか」を議論できるところだと思います。

iOSエンジニアの堤さんもこの勉強会で使用したSwift実践入門でこんな書評を書いていました。
Swiftの各機能が「なぜ」存在し「いつ」使うべきかを解説した技術書 - Swift実践入門

プログラムの文法や書き方を「おまじない」で済まさず、どんな理論で動いているのでどういう場面で使うべきなのかを話せたのはとても有意義な時間でした。

参加者からの発言も多く取り入れて「こういう時使っている」や「ここがわからん」と、どんどん議論を深めて行きました。

改めまして、Swift実践入門読書勉強会をやろうと誘ってくれた、工藤さん、田中さん、会場をいつもお借り頂いてる玉城さん、参加していただいた皆さんにお礼を致します。
ありがとうございました。

次回

GoogleがKotlinを正式なAndroidプログラム言語にしました。

Google、KotlinをAndroidアプリ開発言語に選定――I/O会場から大喝采

時代はKotlinです。 なのでKotlin勉強会を同じメンバーで始めます。

勉強会グループ
Kotlin読書勉強会

第一回目イベント
Kotlinスタートブック読書勉強会 #1

Kotlinを覚えてAndroidアプリを作ろう。
これからもよろしくお願いします!