CombineAsync
CombineAsync 是用于异步任务的 Combine 扩展和工具
示例
要运行示例项目,首先克隆仓库,然后从 Example 目录运行 pod install
要求
- Swift 5.5 或更高版本
- iOS 13.0 或更高版本
- MacOS 10.15 或更高版本
- TVOS 13.0 或更高版本
- WatchOS 8.0 或更高版本
- XCode 13 或更高版本
安装
Cocoapods
CombineAsync 通过 CocoaPods 提供。要安装它,只需将以下行添加到 Podfile 即可
pod 'CombineAsync', '~> 1.2'
从 XCode 使用 Swift 包管理器
- 使用 XCode 菜单 文件 > Swift 包 > 添加包依赖 添加它
- 将 https://github.com/hainayanda/CombineAsync.git 作为 Swift 包 URL 添加
- 在 版本 中设置规则,使用 至下一个主要版本 选项,并将 1.2.0 作为其版本
- 点击下一步并等待
从 Package.swift 使用 Swift 包管理器
在 Package.swift 中将其添加为目标依赖项
dependencies: [
.package(url: "https://github.com/hainayanda/CombineAsync.git", .upToNextMajor(from: "1.2.0"))
]
将 CombineAsync
用作目标中的库
.target(
name: "MyModule",
dependencies: ["CombineAsync"]
)
作者
hainayanda, [email protected]
许可
CombineAsync 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。
使用方法
CombineAsync
包含多个扩展,可用于处理 Combine
和 Swift 异步操作
将发布者转换为异步
您可以使用单个调用将任何实现了 Publisher
的对象转换为 Swift 异步
// implicitly await with 30 second timeout
let result = await publisher.sinkAsynchronously()
// or with timeout explicitly
let timedResult = await publisher.sinkAsynchronously(timeout: 1)
如果在转换过程中发生错误,它将生成 PublisherToAsyncError
。错误是
finishedButNoValue
timeout
failToProduceAnOutput
除这些错误之外,它还会重新抛出原始 Publisher
生成的错误
将发布者序列转换为输出数组
类似于 Publisher
到异步,任何 Publisher
序列都可以通过单个调用转换为异步
let results = await arrayOfPublishers.sinkAsynchronously()
// or with timeout
let timedResults = await arrayOfPublishers.sinkAsynchronously(timeout: 1)
从异步创建 Future
您可以使用提供的便利初始化函数将 Swift 异步转换为 Future
对象
let future = Future {
try await getSomethingAsync()
}
异步发布者吸收器
有时您的吸收器可能执行一些异步操作,并且您真的希望在吸收器中使用 await 而不显式创建 Task。这可以通过调用 asyncSink
来实现
publisher.asyncSink { output in
await somethingAsync(output)
}
自动释放吸收器
您可以使用 autoReleaseSink
忽略可取消的,并期望在完成时移除闭包,因为它会在完成时自动释放
publisher.autoReleaseSink { completion in
// do something on completed
} receiveValue: { output in
// do something to receive value
}
默认情况下,它将在 30 秒后自动释放闭包。
如果您想当某个对象被释放时释放闭包,只需传递该对象即可
publisher.autoReleaseSink(retainedTo: self) { _ in
// do something on completed
} receiveValue: {
// do something on receive value
}
如果您想使用超时释放闭包,只需传递超时即可
publisher.autoReleaseSink(timeout: 60) { _ in
// do something on completed
} receiveValue: {
// do something on receive value
}
无论您传递什么,它都将在满足条件之一时尝试释放闭包
// the closure will be released after completion, or 60 second, or when self is released.
publisher.autoReleaseSink(retainedTo: self, timeout: 60) { _ in
// do something on completed
} receiveValue: {
// do something on receive value
}
如果您需要手动释放,该方法返回一个RetainStateCancellable
对象,这是一个具有RetainState的Cancellable,您可以知道是否已经释放了闭包。
// the closure will be released after completion or 30 seconds, or when the self is released.
let retainCancellable = publisher.autoReleaseSink(retainedTo: self, timeout: 30) { _ in
// do something on completed
} receiveValue: {
// do something on receive value
}
let typeErased = retainCancellable.eraseToAnyCancellable()
...
...
switch retainCancellable.state {
case .retained:
print("closure still retained")
case .released:
print("closure already released")
}
发布者错误恢复
CombineAsync
提供了使用3种其他方法从错误中恢复的方式。
// will ignore error and produce AnyPublisher<Output, Never>
publisher.ignoreError()
// will convert the error to output and produce AnyPublisher<Output, Never>
publisher.replaceError { error in convertErrorToOutput(error) }
// will try to convert the error to output and produce AnyPublisher<Output, Failure>
// if the output is nil, it will just pass the error
publisher.replaceErrorIfNeeded { error in convertErrorToOutputIfNeeded(error) }
这与replaceError
类似,但它接受一个闭包而不是单个输出。
发布者到单个发布者的序列
CombineAsync
为您提供一种将序列中的多个Publisher
合并为一个Publisher
的快捷方式,该Publisher
通过一次调用就发送一个输出数组。
// will collect all the emitted elements from all publishers
let allElementsEmittedPublisher = arrayOfPublishers.merged()
// will collect only the first emitted element from all publishers
let firstElementsEmittedPublisher = arrayOfPublishers.mergedFirsts()
异步映射
CombineAsync
为您提供使用异步映射器进行映射的能力,它将并行运行所有映射并将结果收集起来,同时保持原始顺序。
// map
let mapped = try await arrayOfID.asyncMap { await getUser(with: $0) }
// compact map
let compactMapped = try await arrayOfID.asyncCompactMap { await getUser(with: $0) }
// with timeout
let timedMapped = try await arrayOfID.asyncMap(timeout: 10) { await getUser(with: $0) }
let timedCompactMapped = try await arrayOfID.asyncCompactMap(timeout: 10) { await getUser(with: $0) }
如果您更喜欢使用Publisher
,请将asyncMap
改为futureMap
,将asyncCompactMap
改为futureCompactMap
。
// map
let futureMapped: AnyPublisher<[User], Error> = arrayOfID.futureMap { await getUser(with: $0) }
// compact map
let futureCompactMapped: AnyPublisher<[User], Error> = arrayOfID.futureCompactMap { await getUser(with: $0) }
发布者异步映射
如果您需要异步使用发布者映射,您可以借助CombineAsync
来实现。
publisher.asyncMap { output in
await convertOutputAsynchronously(output)
}
您可以使用的异步映射方法有一些。
asyncMap
与map
等效,但它是异步的。asyncTryMap
与tryMap
等效,但它是异步的。asyncCompactMap
与compactMap
等效,但它是异步的。asyncTryCompactMap
与tryCompactMap
等效,但它是异步的。
发布者映射序列
如果您具有以序列为输出的发布者,并希望在不需要在序列输出上手动调用map的情况下映射每个元素,CombineAsync
将为您提供绕过此方法的能力。
myArrayPublisher.mapSequence { element in
// do map the element of the sequence to another type
}
这些代码行是等效的
myArrayPublisher.map { output in
output.map { element in
// do map the element of the sequence to another type
}
}
您可以使用的所有序列映射器如下
mapSequence(_:)
绕过Sequence.map(_:)
compactMapSequence(_:)
绕过Sequence.compactMap(_:)
tryMapSequence(_:)
绕过Sequence.map(_:)
,但带有抛出映射闭包tryCompactMapSequence(_:)
绕过Sequence.compactMap(_:)
,但带有抛出映射闭包asyncMapSequence(_:)
绕过Sequence.asyncMap(_:)
asyncCompactMapSequence(_:)
绕过Sequence.asyncCompactMap(_:)
贡献
你知道怎么做,只需克隆并提交一个请求