Swift5.0以降でResult型が正式採用されました🎉
この前Loaderを新規で書く機会があって、Result型使えるじゃん! と思って使ってみました。
Result型とは
- success/failureの2パターンだけもつEnum
- エラー処理をswitch文を使ってシンプルに書ける
- 実際の定義は下記
public enum Result<Success, Failure: Error> {
case success(Success)
case failure(Failure)
}
- ジェネリクス型なので、Success/Failureの型は開発者が指定してやる必要がある
- Swift5以前だと、オリジナルでこの手のResult型っぽいEnumを自分で書いていたりライブラリ入れたりしていたと思うので、それはもはや要らない
使い方
たとえばLoaderのDelegateメソッドで、Result型を返す仕様にするとしたら、下記みたいに書きます。
protocol DataLoaderDelegate: AnyObject {
func dataLoader(didReceive result: Result<Data, DataLoaderError>)
}
enum DataLoaderError: Error {
case timeout
case jsonParseFailed
}
この例だと、successのときはData型を返し、failureのときはDataLoaderErrorというカスタムErrorクラスを返します。
ViewController側の受け取り方
さて渡されたResultクラスをどう受け取ればいいかというと、下記みたいな感じで書けます。
// MARK: DataLoaderDelegate
extension ViewController: DataLoaderDelegate {
func dataLoader(didReceive result: Result<Data, DataLoaderError>) {
switch result {
case .success(let data):
self.data = data
case .failure(let error):
print("DataLoaderError:" + error.localizedDescription)
}
}
}
Enumの連想値(Associated Value)をパターンマッチで取り出すのがポイントですね。
この取り出し方、実は今まで知らなかったんですけど、めちゃくちゃ便利ですね。
型情報も失われないのも嬉しい。
使いどころ
Result型自体は失敗する可能性のある非同期処理だけでなく、
try〜catch構文で書いていたようなエラー処理の代替手段にもなります。
↑の記事とかをチラッと読んだ感じだと、非同期処理はResult使うことでコードが読みやすくなる典型例みたいです。
Swiftチーム内でもResultをいつ使うべきか、明確なコンセンサスがないみたいですが、
通常のエラー処理でResultを使うのは非推奨なのは確かなようです。
そうなると現状使いどころってAPI叩くときの非同期処理ぐらいしかユースケースないよな、って思いました。