ClosureChain
ClosureChain
简化 Swift 中连续异步完成方法的顺序。它为连续异步方法提供熟悉的 try-catch 模式。
安装
Swift 包管理器
将 ClosureChain
包添加到应用的 Package.swift
文件中的依赖项中。用最新的 ClosureChain
版本号替换 "x.y.z"。
.package(url: "https://github.com/dannys42/ClosureChain.git", from: "x.y.z")
将 ClosureChain
添加到您的目标依赖项中
.target(name: "example", dependencies: ["ClosureChain"]),
CocoaPods
在 Podfile 中添加 ClosureChain
pod `ClosureChain`
使用说明
在Swift中,通常网络或其他异步方法会使用完成处理程序来简化工作。一个典型的方法签名如下
func someAsyncMethod(_ completion: (Data?, Error?)->Void) {
}
但是,当你需要执行多个异步函数,每个函数都依赖于前一个调用返回的成功数据时,这可能会变得难以管理。
通常,这需要嵌套异步方法或使用状态机。这两者都很难推理。
闭包链通过允许开发者将每个异步调用视为可抛出闭包的连接(即链中的链接),并结合一个单独的catch
闭包来管理任何错误,从而简化了这一点。
简单示例
let chain = ClosureChain()
chain.try { link in
someAsyncMethod() { data, error in
if let error = error {
link.throw(error) // use `link.throw()` since completion block is not throwable
}
guard let data = data else {
link.throw(Failure.missingDdata) // use `link.throw()` since completion block is not throwable
return
}
// do something with `data`
link.success() // required
}
}
chain.catch { error in
// error handler
}
chain.start() // required to start executing links
注意熟悉的try-catch
模式。然而,try
是在链条中的chain
上进行的,而throw
是在link
上进行的。作为一个便利,你可以在try块中直接使用Swift的throw
命令。
有两个额外的必需函数
link.success()
是必需的,以便让ClosureChain
知道异步任务何时完成。chain.start()
是必需的,用于启动链的执行。在发起.start()
命令之前,不会执行任何链接。
传递数据
当我们只有一个异步操作时,上面的例子并不很有用。但如果我们有多个我们要执行的异步操作呢?例如,想象我们尝试执行以下任务序列
- 从网络获取原始图像数据
- 将原始数据转换为UIImage对象。也许这里有一个耗时的异步任务,将执行解密、数字签名验证和JSON反序列化
- 对UIImage进行更多后台处理
- 通知用户已完成操作
为了简单起见,我们假设所有异步方法都使用Result
协议。
这就是如何使用ClosureChain
来看起来的
function closureChainExample() {
let chain = ClosureChain()
chain.try { link in
getDataAsync() { result: Result<Data,Error> in // Result type is provided solely for context in this example
switch result {
case .failure(let error):
link.throw(error) // use link.throw() since completion handler is not throwable
case .success(let data):
link.success(data) // Pass `data` to the next link
}
}
}
chain.try { data: Data, link in // `data` type must match prior link.success() (this check is performed at run-time)
convertToUIImage(data) { result: Result<UIImage,Error> in // Result type is provided solely for context in this example
switch result {
case .failure(let error):
link.throw(error) // use link.throw() since completion handler is not throwable
case .success(let uiimage):
link.success(uiimage) // Pass `uiimage` to the next link
}
}
}
chain.try { image: UIImage, link in // `image` type must match prior link.success()
processImage(image) { error: Error? in // Error type is provided solely for context in this example
do {
if let error = error {
throw(error) // can use do-catch to allow `throws` to pass to `link.throw()`
}
link.success() // Go to next link with no passed data
} catch {
link.throw(error)
}
}
}
chain.try { link in // It is safe to ignore the passed parameter from the last `link.success()`
// Notify the user we're done
link.success() // Required even though this is the last link
}
chain.catch { error in
// error handler
}
chain.start() // Required to start executing links
}
备注
chain
可以安全地转到作用域之外。chain
和相关闭包将在最后一个链接中的link.success()
调用后从内存中删除。- ClosureChains不使用DispatchQueue或OperationQueue。因此,无法保证任何链接在哪个特定的队列/线程上执行。
结果甚至可以更好
如果您异步方法具有单个结果参数的完成处理程序,如上述示例所示,您可以进一步减少您的代码
function closureChainExample() {
let chain = ClosureChain()
chain.try { link in
getDataAsync() { result: Result<Data,Error> in // Result type is provided solely for context in this example
link.return(result) // calls link.throw() or link.success() appropriately
}
}
chain.try { data: Data, link in // `data` type must match prior link.success() (this check is performed at run-time)
convertToUIImage(data) { result: Result<UIImage,Error> in // Result type is provided solely for context in this example
link.return(result) // calls link.throw() or link.success() appropriately
}
}
chain.try { image: UIImage, link in // `image` type must match prior link.success()
processImage(image) { result: Result<UIImage,Error> in // Result type is provided solely for context in this example
link.return(result)
}
}
chain.try { link in // It is safe to ignore the passed parameter from the last `link.success()`
// Notify the user we're done
link.success() // Required even though this is the last link
}
chain.catch { error in
// error handler
}
chain.start() // Required to start executing links
}
API 文档
更多信息请访问我们的 API 参考文档。
授权协议
此库使用 Apache 2.0 许可证授权。完整的许可文本可在 LICENSE 文件中查看。