SwiftでResultをもっと活用する

公開日:2019-01-03
最終更新:2019-01-03
※この記事は外部サイト(https://qiita.com/mishimay/items/f32ea3c33...)からのクロス投稿です

API通信などの非同期処理の結果をenumで定義した Result で受け取る場合が多いと思います。

その Result を活用するためのTips集を見つけたので紹介します。

まずはシンプルなResult

success だけがジェネリックになっています。

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

使用例

func load(handler: @escaping (Result<String>) -> Void) {  
    // ...  
    handler(.success("ok"))  
}  

load { (result) in  
    switch result {  
    case .success(let value):  
        print(value)  
    case .failure(let error):  
        print(error)  
    }  
}

エラーに型を指定する

failure もジェネリックにすることでエラーハンドリングしやすくします。

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

使用例

enum MyError: Error {  
    case error1  
    case error2  
}  

func load(handler: @escaping (Result<String, MyError>) -> Void) {  
    handler(.failure(.error1))  
}  

load { (result) in  
    switch result {  
    case .success(let value):  
        print(value)  
    case .failure(let error):  
        switch error {  
        case .error1:  
            // ...  
        case .error2:  
            // ...  
        }  
    }  
}

複数のErrorをNSErrorにまとめる

複数のシステムAPIを呼ぶなどでErrorの種類がいくつもあるとき、また詳細なErrorハンドリングが必要ではないときには、すべてをNSErrorに変換してしまうのが便利です。

Swift.ErrorNSError へキャストだけで変換可能です。

func load(handler: @escaping (Result<String, NSError>) -> Void) {  
    do {  
        // ...  
        handler(.success("ok"))  
    } catch let error as NSError {  
        handler(.failure(error))  
    }  
}

エラーをthrowできるようにする

以下のような extension を定義すれば、 try で値を取り出し catch でエラーを捕まえることができます。

extension Result {  
    func resolve() throws -> T {  
        switch self {  
        case .success(let value):  
            return value  
        case .failure(let error):  
            throw error  
        }  
    }  
}

使用例

load { (result) in  
    do {  
        let value = try result.resolve()  
        print(value)  
    } catch {  
        print(error)  
    }  
}

他にも任意のextensionを生やす

よく使う処理を extension として定義して使いまわしましょう。

extension Result where T == String {  
    func decoded<U: Decodable>() throws -> U {  
        let decoder = JSONDecoder()  
        let value = try resolve()  
        let data = value.data(using: .utf8) ?? Data()  
        return try decoder.decode(U.self, from: data)  
    }  
}

使用例

load { (result) in  
    do {  
        let value = try result.decoded() as User  
        print(value)  
    } catch {  
        print(error)  
    }  
}

環境

Apple Swift version 4.2 (swiftlang-1000.11.37.1 clang-1000.11.45.1)  
Target: x86_64-apple-darwin18.2.0

参考文献

The power of Result types in Swift Swift by Sundell

記事が少しでもいいなと思ったらクラップを送ってみよう!
36
+1
@mishimayの技術ブログ

よく一緒に読まれている記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる

技術ブログをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

技術ブログを開設する

Qrunchでアウトプットをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

Markdownで書ける

ログ機能でアウトプットを加速

デザインのカスタマイズが可能

技術ブログ開設

ここから先はアカウント(ブログ)開設が必要です

英数字4文字以上
.qrunch.io
英数字6文字以上
ログインする