CombineAsync 2.0.0

CombineAsync 2.0.0

Nayanda Haberty 维护。



  • hainayanda

CombineAsync

CombineAsync 是用于异步任务的 Combine 扩展和工具

Codacy Badge build test SwiftPM Compatible Version License Platform

示例

要运行示例项目,首先克隆仓库,然后从 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)
}

您可以使用的异步映射方法有一些。

  • asyncMapmap等效,但它是异步的。
  • asyncTryMaptryMap等效,但它是异步的。
  • asyncCompactMapcompactMap等效,但它是异步的。
  • asyncTryCompactMaptryCompactMap等效,但它是异步的。

发布者映射序列

如果您具有以序列为输出的发布者,并希望在不需要在序列输出上手动调用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(_:)

贡献

你知道怎么做,只需克隆并提交一个请求