Result
这是提供Result<Value, Error>
的Swift µ框架。
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
提供的默认索引封装更加健壮。它不是返回Any?
,而是返回一个包含给定键的String
值或详细说明了出了什么错误的ErrorType
的Result
。
处理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文档。
结果与抛出异常
Swift 2.0 引入通过 throw
和 catch
来处理错误。通过封装结果而不是篡改控制流,《Result
》 实现了相同的目标。这种 《Result
》 抽象提供了强大的功能,如 map
和 flatMap
,使 《Result
》 组合能力比 throw
更强。
由于处理抛出异常的 API 非常常见,您可以通过使用 materialize
方法将这些函数转换为 《Result
》。反之,您可以通过调用 dematerialize
来使用 《Result
》 抛出一个错误。
高阶函数
map
和 flatMap
与 Optional.map
和 Optional.flatMap
操作相同,只是它们应用于 Result
。
map
将 Result
转换为具有新类型的新 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
在转换 Result
成为另一个 Result
方面类似于 map
,但是传递给 flatMap
的函数必须返回一个 Result
。
关于 map
和 flatMap
的详细讨论超出了本文档的范围。如果您想要更深入的了解,请阅读有关函子和单子的内容。本文是一个很好的起点 这里。
整合
Carthage
- 将此仓库作为子模块添加,或将其添加到您的 Cartfile 中,如果正在使用 Carthage 来管理依赖项。
- 将
Result.xcodeproj
拖动到您的项目或工作区中。 - 将您的目标与
Result.framework
相关联。 - 应用程序目标应确保框架被复制到它们的应用程序包中。(框架目标应要求与之链接的应用程序包含 Result。)
CocoaPods
pod 'Result', '~> 4.0.0'
Swift Package Manager
import PackageDescription
let package = Package(
name: "MyProject",
targets: [],
dependencies: [
.Package(url: "https://github.com/antitypical/Result.git",
majorVersion: 4)
]
)