Swift5のResultはこう使う！

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

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

# 0.この関数を使って遊んだ
よく使う感じの、結果をクロージャで返す関数です。即時実行されるので、`escaping` はしません。

```swift: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` で書き分けるいつもの方法です。いつもどおり使えるのは大事です。

```swift: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

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

# 3. mapで遊んだ
新しく使えるようになった[map(_:)](https://developer.apple.com/documentation/swift/result/3139400-map)メソッドでは、結果がsuccessの場合に関数適応した新しいResultを返せます。この場合は、成功時の値が `hoge` のReusult型から、その後に `fuga`を加えて `hoge fuga` を持つResultを新しく返しています。

他にも[mapError(_:)](https://developer.apple.com/documentation/swift/result/3139402-maperror), [flatMap(_:)](https://developer.apple.com/documentation/swift/result/3139395-flatmap), [flatMapError(_:)](https://developer.apple.com/documentation/swift/result/3139396-flatmaperror)などの親戚がいます。

```swift: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: )](https://developer.apple.com/documentation/swift/result/3139399-init)
というイニシャライザが`Result` には用意されており、クロージャ内で `return` するとsuccess、`throw` するとfailureとして`Result`を初期化できます。

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

```swift: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も同様の実装がされています。

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

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

```swift: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早く使いたい。**

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