结果

Build Status Carthage compatible CocoaPods Reference Status

这是一个 Swift 微框架,提供 Result<Value, Error>

Result<Value, Error> 的值可以是成功的(包装 Value)或者失败的(包装 Error)。这类似于 Swift 的原生 Optional 类型:success 类似于 some,而 failure 类似于 none,但带有关联的 Error 值。添加关联的 Error 允许传递错误以进行日志记录或向用户显示。

使用此微框架而不是自己编写 Result 类型,可以方便地与其他也使用 Result 的框架进行交互。

使用

当操作有可能失败时,请使用 Result。考虑以下示例,该函数尝试从 JSON Dictionary 中提取给定键的 String

typealias JSONObject = [String: Any]

enum JSONError: Error {
    case noSuchKey(String)
    case typeMismatch
}

func stringForKey(json: JSONObject, key: String) -> Result<String, JSONError> {
    guard let value = json[key] else {
        return .failure(.noSuchKey(key))
    }
    
    guard let value = value as? String else {
        return .failure(.typeMismatch)
    }

    return .success(value)
}

此函数提供了对 Dictionary 提供的默认下标的更健壮的包装。它返回一个 Result,而不是返回 Any?,该 Result 要么包含给定键的 String 值,要么包含详细说明出错原因的 ErrorType

处理 Result 的一种简单方法是使用 switch 语句对其进行解构。

switch stringForKey(json, key: "email") {

case let .success(email):
    print("The email is \(email)")
    
case let .failure(.noSuchKey(key)):
    print("\(key) is not a valid key")
    
case .failure(.typeMismatch):
    print("Didn't have the right type")
}

使用 switch 语句可以进行强大的模式匹配,并确保涵盖所有可能的结果。 Swift 2.0 提供了新的方法来解构像 if-case 语句这样的枚举,但要注意,这种方法不能确保处理所有错误。

有关处理 Result 的其他可用方法,请参见 API 文档

Result vs. Throws

Swift 2.0 通过抛出和捕获 Error 引入了错误处理。 Result 通过封装结果而不是劫持控制流来实现相同的目标。 Result 抽象启用了强大的功能,例如 mapflatMap,使 Resultthrow 更具可组合性。

由于处理抛出异常的 API 很常见,因此您可以使用 materialize 方法将此类函数转换为 Result。 相反,可以通过调用 dematerialize 来使用 Result 抛出错误。

高阶函数

mapflatMap 的操作方式与 Optional.mapOptional.flatMap 相同,只不过它们应用于 Result

mapResult 转换为新类型的 Result。 它通过使用将 Value 类型转换为新值的函数来实现。 此转换仅在 success 的情况下应用。 在 failure 的情况下,关联的错误将重新包装在新 Result 中。

// transforms a Result<Int, JSONError> to a Result<String, JSONError>
let idResult = intForKey(json, key:"id").map { id in String(id) }

在此,最终结果要么是作为 String 的 id,要么从先前的结果中继承 failure

flatMap 类似于 map,因为它将 Result 转换为另一个 Result。 但是,传递给 flatMap 的函数必须返回一个 Result

深入讨论 mapflatMap 超出了本文档的范围。 如果您想更深入地了解,请阅读有关 functors 和 monads 的信息。 这篇文章是一个很好的 起点

集成

Carthage

  1. 如果您使用 carthage 来管理您的依赖项,请将此存储库添加为子模块和/或 添加到您的 Cartfile
  2. Result.xcodeproj 拖到您的项目或工作区中。
  3. 将您的目标链接到 Result.framework
  4. 应用程序目标应确保该框架已复制到其应用程序捆绑包中。(框架目标应改为要求链接它们的应用程序包括 Result。)

Cocoapods

pod 'Result', '~> 5.0'

Swift Package Manager

// swift-tools-version:4.0
import PackageDescription

let package = Package(
    name: "MyProject",
    targets: [],
    dependencies: [
        .package(url: "https://github.com/antitypical/Result.git",
                 from: "5.0.0")
    ]
)