Swift 5.0 值得關(guān)注的特性:增加 Result<T, E: Error> 枚舉類型

HackingSwift: What’s new in Swift 5.0
Result<T> 還是 Result<T, E: Error>

背景

在異步獲取數(shù)據(jù)的場(chǎng)景中,常見(jiàn)的回調(diào)的數(shù)據(jù)結(jié)構(gòu)是這樣的:表示獲取成功的數(shù)據(jù),表示獲取失敗的 error。因?yàn)閿?shù)據(jù)可能獲取成功,也可能失敗。因此回調(diào)中的數(shù)據(jù)和錯(cuò)誤都是 optional 類型。
比如 CloudKit 中保存數(shù)據(jù)的一個(gè)函數(shù)就是這樣:

func save(_ record: CKRecord, completionHandler: @escaping (CKRecord?, Error?) -> Void)

這種形式的缺點(diǎn)是沒(méi)有體現(xiàn)出兩種結(jié)果的互斥關(guān)系:如果數(shù)據(jù)成功獲取到了,那么 error 一定為空。如果 error 有值,數(shù)據(jù)一定是獲取失敗了。

Swift 中枚舉的能力相比 OC 有著很大的進(jìn)步,每個(gè)枚舉值除了可以是常規(guī)的基礎(chǔ)類型,還可以是一個(gè)關(guān)聯(lián)的類型。有了這樣的特性后用枚舉來(lái)優(yōu)化返回結(jié)果的數(shù)據(jù)結(jié)構(gòu)顯得水到渠成:

enum Result<Success, Failure> where Failure : Error {

    /// A success, storing a `Success` value.
    case success(Success)

    /// A failure, storing a `Failure` value.
    case failure(Failure)
}

基本用法

定義異步返回結(jié)果是 Int 類型的函數(shù):

func fetchData(_ completionHandler: @escaping (Result<Int, Error>) -> Void) {
    DispatchQueue.global().async {
        let isSuccess = true
        if isSuccess {
            let resultValue = 6
            return completionHandler(.success(resultValue))
        } else {
            let error = NSError(domain: "custom error", code: -1, userInfo: nil)
            return completionHandler(.failure(error))
        }
    }
}

返回值的類型通過(guò)泛型進(jìn)行約束,Result 第一個(gè)泛型類型表示返回值的類型,第二個(gè)類型表示錯(cuò)誤的類型。對(duì) Result 賦值和常規(guī)的枚舉一樣:

let valueResult: Result<Int, CustomError> = Result.success(4)

// 因?yàn)?swift 中會(huì)進(jìn)行類型推斷,編譯器在確認(rèn)返回的是 `Result` 類型后,可以省略枚舉類型的聲明
let errorResult = .failure(CustomError.inputNotValid)

取出 Result 值和獲取普通的關(guān)聯(lián)類型枚舉是一樣的:

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

如果你只想要獲取其中一項(xiàng)的值,也可以直接用 if case 拆包:

fetchDate { (result) in
    if case .success(let value) = result {
        print(value)
    }
}

可以判等

Enum 是一個(gè)值類型,是一個(gè)值就應(yīng)該可以判斷是否相等。如果 Result 的成功和失敗的類型都是 Equatable,那么 Result就可以判等,源碼如下:

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

類似的,如果是成功和失敗的類型都是 Hashable,那么 Result 也是 Hashable

extension Result : Hashable where Success : Hashable, Failure : Hashable { }

如果實(shí)現(xiàn)了 Hashable ,可以用來(lái)當(dāng)做字典的 key。

輔助的 API

map、mapError

與 Dictionary 類似,Swift 為 Result 提供了幾個(gè) map value 和 error 的方法。

let intResult: Result<Int, Error> = Result.success(4)
let stringResult = x.map { (value) -> Result<String, Error> in
    return  .success("map")
}

let originError = NSError(domain: "origin error", code: -1, userInfo: nil)
let errorResult: Result<Int, Error> = .failure(originError)
let newErrorResult = errorResult.mapError { (error) -> Error in
    let newError = NSError(domain: "new error", code: -2, userInfo: nil)
    return newError
}

flatMap、flatMapError

map 返回的是具體的結(jié)果和錯(cuò)誤, flatMap 閉包中返回的是 Result 類型。如果 Result 中包含的是數(shù)據(jù),效果和 map 一致,替換數(shù)據(jù);如果 Result 中包含的是錯(cuò)誤,那么不替換結(jié)果。

let intResult: Result<Int, Error> = Result.success(4)

// 替換成功
let flatMapResult = intResult.flatMap { (value) -> Result<String, Error> in
    return  .success("flatMap")
}

// 沒(méi)有執(zhí)行替換操作,flatMapIntResult 值還是 intResult
let flatMapIntResult = intResult.flatMap { (value) -> Result<String, Error> in
    return  .failure(NSError(domain: "origin error", code: -1, userInfo: nil))
}

get

很多時(shí)候只關(guān)心 Result 的值,Swift 提供了 get() 函數(shù)來(lái)便捷的直接獲取值,需要注意的是這個(gè)函數(shù)被標(biāo)記為 throws,使用時(shí)語(yǔ)句前需要加上 try

let intResult: Result<Int, Error> = Result.success(4)

let value = try? intResult.get()

可拋出異常的閉包初始化器

很多時(shí)候獲取返回值的閉包中可能會(huì)發(fā)生異常代表獲取失敗的錯(cuò)誤,基于這個(gè)場(chǎng)景 Swift 提供了一個(gè)可拋出異常的閉包初始化器:

enum CustomError: Error, Equatable {
    case inputNotValid
}

let fetchInt = { () -> Int in
    if true {
        return 4
    } else {
        throw CustomError.inputNotValid
    }
}

let result: Result<Int, Error> = Result { try fetchInt() }

需要提醒是通過(guò)這種方式聲明的 Result 的 error 類型只能是 Error,不能指定特定的 Error


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容