Swift 5.0 值得关注的特性:增加 Result 枚举类型

HackingSwift: What’s new in Swift 5.0

Result 还是 Result

背景

在异步获取数据的场景中,常见的回调的数据结构是这样的:表示获取成功的数据,表示获取失败的 error。因为数据可能获取成功,也可能失败。因此回调中的数据和错误都是 optional 类型。

比如 CloudKit 中保存数据的一个函数就是这样:

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

这种形式的缺点是没有体现出两种结果的互斥关系:如果数据成功获取到了,那么 error 一定为空。如果 error 有值,数据一定是获取失败了。

Swift 中枚举的能力相比 OC 有着很大的进步,每个枚举值除了可以是常规的基础类型,还可以是一个关联的类型。有了这样的特性后用枚举来优化返回结果的数据结构显得水到渠成:

enum Result where Failure : Error {    /// A success, storing a `Success` value.    case success(Success)        /// A failure, storing a `Failure` value.    case failure(Failure)}

基本用法

定义异步返回结果是 Int 类型的函数:

func fetchData(_ completionHandler: @escaping (Result) -> 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))        }    }}

返回值的类型通过泛型进行约束,Result 第一个泛型类型表示返回值的类型,第二个类型表示错误的类型。对 Result  赋值和常规的枚举一样:

let valueResult: Result = Result.success(4)// 因为 swift 中会进行类型推断,编译器在确认返回的是 `Result` 类型后,可以省略枚举类型的声明let errorResult = .failure(CustomError.inputNotValid)

取出 Result  值和获取普通的关联类型枚举是一样的:

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

如果你只想要获取其中一项的值,也可以直接用 if case 拆包:

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

可以判等

Enum 是一个值类型,是一个值就应该可以判断是否相等。如果 Result 的成功和失败的类型都是 Equatable,那么 Result就可以判等,源码如下:

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

类似的,如果是成功和失败的类型都是 Hashable,那么 Result 也是 Hashable:

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

如果实现了 Hashable ,可以用来当做字典的 key。

辅助的 API

map、mapError

与 Dictionary 类似,Swift 为 Result 提供了几个 map value 和 error 的方法。

let intResult: Result = Result.success(4)let stringResult = x.map { (value) -> Result in    return  .success("map")}let originError = NSError(domain: "origin error", code: -1, userInfo: nil)let errorResult: Result = .failure(originError)let newErrorResult = errorResult.mapError { (error) -> Error in    let newError = NSError(domain: "new error", code: -2, userInfo: nil)    return newError}

flatMap、flatMapError

map 返回的是具体的结果和错误, flatMap 闭包中返回的是 Result 类型。如果 Result 中包含的是数据,效果和 map 一致,替换数据;如果 Result 中包含的是错误,那么不替换结果。

let intResult: Result = Result.success(4)// 替换成功let flatMapResult = intResult.flatMap { (value) -> Result in    return  .success("flatMap")}// 没有执行替换操作,flatMapIntResult 值还是 intResultlet flatMapIntResult = intResult.flatMap { (value) -> Result in    return  .failure(NSError(domain: "origin error", code: -1, userInfo: nil))}

get

很多时候只关心  Result 的值,Swift 提供了 get() 函数来便捷的直接获取值,需要注意的是这个函数被标记为 throws,使用时语句前需要加上 try:

let intResult: Result = Result.success(4)let value = try? intResult.get()

可抛出异常的闭包初始化器

很多时候获取返回值的闭包中可能会发生异常代表获取失败的错误,基于这个场景 Swift 提供了一个可抛出异常的闭包初始化器:

enum CustomError: Error, Equatable {    case inputNotValid}let fetchInt = { () -> Int in    if true {        return 4    } else {        throw CustomError.inputNotValid    }}let result: Result = Result { try fetchInt() }

需要提醒是通过这种方式声明的 Result 的 error 类型只能是 Error,不能指定特定的 Error。


微博:@没故事的卓同学

掘金博客

如果想与我有更密切的交流也可以加入我的知识星球:iOS 程序员保护协会

作者:没故事的卓同学  交流群号: 869685378   寻ios马甲包技术大大,上架cp qp应用 Q 51259559 长期合作

猜你喜欢

转载自blog.csdn.net/ios8988/article/details/89477991