build status macOS iOS Linux Apache 2

ClosureChain

ClosureChain 简化了 Swift 中顺序异步完成方法的处理。 它为顺序异步方法提供了一种熟悉的 try-catch 模式。

安装

Swift Package Manager

ClosureChain 包添加到应用程序的 Package.swift 文件中的依赖项。 将 "x.y.z" 替换为最新的 ClosureChain 版本

.package(url: "https://github.com/dannys42/ClosureChain.git", from: "x.y.z")

ClosureChain 添加到你的 target 的依赖项中

.target(name: "example", dependencies: ["ClosureChain"]),

Cocoapods

ClosureChain 添加到你的 Podfile 中

pod `ClosureChain`

用法

通常在 Swift 中,网络或其他异步方法会使用完成处理程序来简化工作。 一个典型的方法签名如下所示:

    func someAsyncMethod(_ completion: (Data?, Error?)->Void) {
    }

但是,当您需要执行多个异步函数,每个函数都依赖于前一次调用的成功数据时,这可能会变得难以管理。

通常,这需要嵌套异步方法或使用状态机。 这两者都难以理解。

Closure Chains 通过允许开发人员将每个异步调用视为可抛出闭包的链接(即链中的链接),并使用单个 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 命令。

还有两个额外的必需函数

传递数据

当我们只有一个异步操作时,以上内容不是很有用。 但是,如果我们有几个我们希望执行的异步操作该怎么办? 例如,假设我们尝试执行以下任务序列

为简单起见,我们假设我们所有的异步方法都使用 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
}

注意事项

结果可以更好

如果您的异步方法具有带有单个 Result 参数的完成处理程序,例如

    func someAsyncMethod(_ completion: (Result<Data, Error>)->Void) {
    }

您可以进一步简化代码

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 中找到。