SwiftUseCase

Swift platforms Swift Package Manager compatible GitHub Current version

SwiftUseCase 是一个用于创建独立且可测试覆盖的用例库,并具有强大的执行 API。

安装

您可以使用 Swift Package Manager 来集成该库,方法是在您的 Package.swift 文件中添加以下依赖项,或直接在 Xcode 中添加

.package(url: "https://github.com/xtro/SwiftUseCase.git", .upToNextMajor(from: "0.0.1"))

定义

一个 UseCase 是一个简单的容器,包含一个 ParameterResult 类型,并执行一个 Execution

public protocol UseCaseable {
    associatedtype Parameter: Sendable
    associatedtype Result: Sendable
    associatedtype Execution: Sendable
    var execute: Execution { get }
}

共有四种类型的执行

public typealias Executable<Parameter: Sendable, Result: Sendable> = @Sendable (Parameter) -> Result
public typealias AsyncExecutable<Parameter: Sendable, Result: Sendable> = @Sendable (Parameter) async -> Result
public typealias ThrowingExecutable<Parameter: Sendable, Result: Sendable> = @Sendable (Parameter) throws -> Result
public typealias AsyncThrowingExecutable<Parameter: Sendable, Result: Sendable> = @Sendable (Parameter) async throws -> Result

开始使用

在这个例子中,我们使用 swift concurrency 实现了一个数据任务。参数是 URLRequest 和 URLSession

import SwiftUseCase

public enum Network {
    public struct DataTask: AsyncThrowingUseCase {
        public struct Parameter {
            let request: URLRequest
            let session: URLSession
        }
        public typealias Result = (response: HTTPURLResponse, data: Data)
        
        public var execute: AsyncThrowingExecutable<Parameter, Result> = { parameter in
            class CancellableWrapper {
                var dataTask: URLSessionDataTask?
            }
            let urlSessionTask = CancellableWrapper()
            return try await withTaskCancellationHandler {
                return try await withUnsafeThrowingContinuation { continuation in
                    urlSessionTask.dataTask = parameter.session.dataTask(with: parameter.request) { data, response, error in
                        if let error = error {
                            continuation.resume(throwing: error)
                        }
                        if let data = data {
                            let result = (response as! HTTPURLResponse, data)
                            continuation.resume(returning: result)
                        }
                    }
                    urlSessionTask.dataTask?.resume()
                }
            } onCancel: {
                urlSessionTask.dataTask?.cancel()
            }
        }
    }
}

之后,我们可以通过多种方式使用已实现的代码。

用作 Combine 发布者

let usecase = Network.DataTask()
usecase.publisher(
    .init(
        request: URLRequest(url: URL(string: path)!),
        session: session ?? .shared
    )
)

看起来不错,但在这种情况下,参数和初始化太复杂了,我们可以通过编写像这样的扩展来简化它

extension Network.DataTask.Parameter {
    static func get(_ path: String, session: URLSession? = nil) -> Self {
        .init(
            request: URLRequest(url: URL(string: path)!),
            session: session ?? .shared
        )
    }
}
extension Network {
    static var dataTask: DataTask {
        DataTask()
    }
}

好多了,现在我们可以用作 Combine 发布者

Network.dataTask
    .publisher( .get("https://api.coincap.io/v2/assets") )
    ...

或使用 blocks

Network.dataTask( .get("https://api.coincap.io/v2/assets") ) { result in
   ...
}

或在 swift concurrency 中

let result = try await Network.dataTask( .get("https://api.coincap.io/v2/assets") )

最后,每个 UseCase 都可以转换为 AnyUseCase,这是一种用例的类型擦除表示

let usecase = MyUseCase().eraseToAnyUseCase
usecase.onComplete = {
    print("Result: \($0)")
}
usecase.onFailure = {
    print("Failure: \($0)")
}
usecase(parameter)

赞助商

SwiftUseCase 是一个 MIT 许可的开源项目,其持续开发完全由出色的支持者的支持成为可能。如果您想加入他们,请考虑赞助此开发。

贡献

欢迎提交 Pull Request。对于重大更改,请先打开一个 issue 讨论您想要更改的内容。

请确保根据需要更新测试。

许可证

此库根据 MIT 许可证发布。有关详细信息,请参阅 LICENSE。