LoginSignup
77
42

More than 5 years have passed since last update.

Swift5のResultはこう使う!

Last updated at Posted at 2019-03-26

Swift5のResultが楽しそうだったので、色々遊んでみました。
Xcode10.2になって、playgroundの挙動も改善してますね。以前は怪しい挙動をよく見かけましたが、今のところ大丈夫そう。

Appleの公式リファレンスはこちらになります。
https://developer.apple.com/documentation/swift/result

0.この関数を使って遊んだ

よく使う感じの、結果をクロージャで返す関数です。即時実行されるので、escaping はしません。

ResultPlayground.swift
import UIKit

struct EmptyError: Error { }

extension EmptyError: CustomStringConvertible {
    var description: String {
        return "value is Empty!"
    }
}

// 関数定義
func resultTest(value: String, completion: (Result<String, Error>) -> Void) {

    if !value.isEmpty {
        completion(.success("Aurgument you pass is \(value)"))
    } else {
        completion(.failure(EmptyError()))
    }

}

1. いつも通りのResultで遊んだ

switch で書き分けるいつもの方法です。いつもどおり使えるのは大事です。

Result.swift
// 準正常系テスト、いつも通りのResultの使い方
resultTest(value: "") { (result) in
    switch result {
    case .success(let value):
        print(value)
    case .failure(let error):
        print(error)
    }
}
// output: value is Empty!

2. get()で遊んだ

Swift5で追加された Result にはget()メソッドがあり、resultを do/catch に変換できます。manual prop から auto prop への変換メソッドとも言えるような気がします。

参考: https://qiita.com/koher/items/7e92414082476fb87b76#manual-propagation-と-automatic-propagation

get.swift
// 正常系
resultTest(value: "テストだよん!") { (result) in
    do {
        print(try result.get())
    } catch {
        print(error)
    }
}
// output: Aurgument you pass is テストだよん!

3. mapで遊んだ

新しく使えるようになったmap(_:)メソッドでは、結果がsuccessの場合に関数適応した新しいResultを返せます。この場合は、成功時の値が hoge のReusult型から、その後に fugaを加えて hoge fuga を持つResultを新しく返しています。

他にもmapError(_:), flatMap(_:), flatMapError(_:)などの親戚がいます。

map.swift
resultTest(value: "hoge") { (result) in
    let newResult = result.map { $0 + " fuga" }
    switch newResult {
    case .success(let value):
        print(value)
    case .failure(let error):
        print(error)
    }
}
// output: Aurgument you pass is hoge fuga

4. 初期化で遊んだ

init(catching: )
というイニシャライザがResult には用意されており、クロージャ内で return するとsuccess、throw するとfailureとしてResultを初期化できます。

いまいちユースケースが思いつかないので、詳しい方コメント頂けると助かります!

ThrowingInit.swift
var optionalInt: Int? = 2
let result = Result<Int, Error> { () -> Int in
    guard let int = optionalInt else {
        throw NSError(domain: "OptionalError", code: 111, userInfo: nil) // 適当に返してる
    }
    return int
}

print(try! result.get())
// output: 2

5. Equatableで遊んだ

条件付き適合として以下のように定義されており、SuccessとFailureの両方がEquatableに適合している場合は、Result自体が同一視可能になります。(Hashableも同様の実装がされています。

Result.swift
extension Result : Equatable where Success : Equatable, Failure : Equatable { }

適当なエラーを作って Equatable に適合させてみると、比較が出来るようになりました。

compare.swift
struct UnderZeroError: Error {
    let value: Int

    var localizedDescription: String {
        return "\(value) is under zero."
    }
}

extension UnderZeroError: Equatable { }

let comparableResultOne: Result<Int, UnderZeroError> = .success(3)
let comparableResultTwo:  Result<Int, UnderZeroError> = .success(3)

print(comparableResultOne == comparableResultTwo)
// output: true

let comparableFailureResultOne: Result<Int, UnderZeroError> = .failure(UnderZeroError(value: -2))
let comparableFailureResultTwo:  Result<Int, UnderZeroError> = .failure(UnderZeroError(value: -3))

print(comparableFailureResultOne == comparableFailureResultTwo)
// output: false

6. 遊んだ感想

機能が多すぎず足りなすぎず、公式対応として ちょうどいい感じのResult が提供されたように感じます。

async/await早く使いたい。

  終
制作・著作
━━━━━
 ⓃⒽⓀ

77
42
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
77
42