gpt4 book ai didi

swift - 如何转换为/从 AnyPublisher 和 AnyPublisher

转载 作者:行者123 更新时间:2023-12-03 23:45:52 26 4
gpt4 key购买 nike

我将异步请求包装在 Combine 发布者中,以便它们可以轻松地跨不同管道使用。
消费者可能会按以下方式保留这些发布者:

struct Dependencies {
var loadImageRequest: AnyPublisher<UIImage, Never>
var saveToDatabaseRequest: AnyPublisher<Void, Never>
var saveToUserDefaultsRequest: AnyPublisher<Never, Never>
}
两种更常见的请求类型是:
  • 开火即忘(立即完成) :例如,将值保存到用户默认值,可以建模为开火即忘。到目前为止,AnyPublisher<Never, Never> 似乎是表达这种类型的好方法。这可以通过 Empty<Never, Never>(completeImmediately: true) 轻松构建。
  • 触发、等待并忽略结果 :一个例子是将一个值保存到数据库(并忽略结果),但仍想等到保存完成后再继续管道。我一直在使用 AnyPublisher<Void, Never> 对这些请求类型进行建模。构建这些的一种简单方法是通过 Future<Void, Never>() { promise in promise(.success(()))}

  • 这两者都有一个共同的主题,即忽略结果。因此,当将这些交给消费者时,有时在这两种数据类型之间进行转换很有用: AnyPublisher<Never, Never>AnyPublisher<Void, Never>
    有三种可能的方法可以在两者之间进行转换:
  • Never -> Void ,立即完成
    转换它的一种方法是强制转换:
    let neverPublisher: AnyPublisher<Never, Never> = ...
    let voidPublisher: AnyPublisher<Void, Never> = neverPublisher
    .map { _ in () }
    .append(Just(()))
    .eraseToAnyPublisher()
  • Void -> Never , 等到 Void 完成
    由于有一个内置运算符,因此转换很容易:
    let voidPublisher: AnyPublisher<Void, Never> = ...
    let neverPublisher: AnyPublisher<Never, Never> = voidPublisher
    .ignoreOutput()
    .eraseToAnyPublisher()
  • Void -> Never ,立即完成
    我不确定进行这种转换的最佳方法。我想出的解决方案有两个主要缺点:使用 handleEvents ,并且需要在某处定义 cancellables :
    let cancellables: Set<AnyCancellable> = []
    let voidPublisher: AnyPublisher<Void, Never> = ...
    let neverPublisher: AnyPublisher<Never, Never> = Empty<Never, Never>(completeImmediately: true)
    .handleEvents(receiveCompletion: { _ in voidPublisher.sink(receiveValue: { _ in }).store(in: &cancellables) })
    .eraseToAnyPublisher()

  • 问题:
  • 是否有更好的方法来进行转换 #1( Never -> Void ,立即完成)而无需同时调用 mapappend ? (例如,类似于如何使用 ignoreOutput 来解决第二次转换?)
  • 有没有更好的方法来进行不需要 Void -> Never 的第三次转换( cancellables ,立即完成)?
  • 最佳答案

    我还没有找到更优雅的方法来做#2。
    这是一个更好的 #3 ( Void -> Never ,立即完成) 解决方案:

    let voidPublisher: AnyPublisher<Void, Never> = ...
    let neverPublisher: AnyPublisher<Never, Never> = Publishers.Merge(Just<Void>(()), saveToDatabase)
    .first()
    .ignoreOutput()
    .eraseToAnyPublisher()

    这是我用来确保一切按预期运行的 timing Playground:
    import Combine
    import Foundation

    let delaySec: TimeInterval = 0.1
    let halfDelaySec: TimeInterval = delaySec / 2
    let halfDelayMicroSeconds: useconds_t = useconds_t(halfDelaySec * 1_000_000)
    let sleepBufferMicroSeconds: useconds_t = useconds_t(0.01 * 1_000_000)

    var cancellables = [AnyCancellable]()
    var output: [String] = []

    func performVoidAction(voidPublisher: AnyPublisher<Void, Never>) {
    voidPublisher
    .handleEvents(
    receiveCompletion: { _ in output.append("performVoidAction - completion") },
    receiveCancel: { output.append("performVoidAction - cancel") })
    .sink(receiveValue: { output.append("performVoidAction - sink") })
    .store(in: &cancellables)
    }

    func performNeverAction(neverPublisher: AnyPublisher<Never, Never>) {
    neverPublisher
    .handleEvents(
    receiveCompletion: { _ in output.append("performNeverAction - completion") },
    receiveCancel: { output.append("performNeverAction - cancel") })
    .sink(receiveValue: { _ in output.append("performNeverAction - sink") })
    .store(in: &cancellables)
    }

    func makeSaveToDatabasePublisher() -> AnyPublisher<Void, Never> {
    Deferred { _saveToDatabase() }.eraseToAnyPublisher()
    }

    func makeSaveToUserDefaultsPublisher() -> AnyPublisher<Never, Never> {
    Deferred { _saveToUserDefaults() }.eraseToAnyPublisher()
    }

    // --->(Void)|
    // AnyPublisher<Void, Never> wraps an API that does something and finishes upon completion.
    private func _saveToDatabase() -> AnyPublisher<Void, Never> {
    return Future<Void, Never> { promise in
    output.append("saving to database")
    DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: DispatchTime.now() + delaySec) {
    output.append("saved to database")
    promise(.success(()))
    }
    }.eraseToAnyPublisher()
    }

    // |
    // AnyPublisher<Never, Never> wraps an API that does something and completes immediately, it does not wait for completion.
    private func _saveToUserDefaults() -> AnyPublisher<Never, Never> {
    output.append("saved to user defaults")
    return Empty<Never, Never>(completeImmediately: true)
    .eraseToAnyPublisher()
    }

    func assert(_ value: Bool) -> String {
    value ? "✅" : "❌"
    }

    // tests
    assert(output.isEmpty)

    var saveToDatabase = makeSaveToDatabasePublisher()
    assert(output.isEmpty, "It should not fire the action yet.")

    // verify database save, first time
    performVoidAction(voidPublisher: saveToDatabase)
    assert(!output.isEmpty && output.removeFirst() == "saving to database")
    assert(output.isEmpty)
    usleep(halfDelayMicroSeconds + sleepBufferMicroSeconds)
    assert(output.isEmpty)
    usleep(halfDelayMicroSeconds + sleepBufferMicroSeconds)
    assert(!output.isEmpty && output.removeFirst() == "saved to database")
    assert(!output.isEmpty && output.removeFirst() == "performVoidAction - sink")
    assert(!output.isEmpty && output.removeFirst() == "performVoidAction - completion")
    assert(output.isEmpty)

    // verify database save, second time
    performVoidAction(voidPublisher: saveToDatabase)
    assert(!output.isEmpty && output.removeFirst() == "saving to database")
    assert(output.isEmpty)
    usleep(halfDelayMicroSeconds + sleepBufferMicroSeconds)
    assert(output.isEmpty)
    usleep(halfDelayMicroSeconds + sleepBufferMicroSeconds)
    assert(!output.isEmpty && output.removeFirst() == "saved to database")
    assert(!output.isEmpty && output.removeFirst() == "performVoidAction - sink")
    assert(!output.isEmpty && output.removeFirst() == "performVoidAction - completion")
    assert(output.isEmpty)

    var saveToUserDefaults = makeSaveToUserDefaultsPublisher()
    assert(output.isEmpty, "It should not fire the action yet.")

    // verify user defaults save, first time
    performNeverAction(neverPublisher: saveToUserDefaults)
    assert(!output.isEmpty && output.removeFirst() == "saved to user defaults")
    assert(!output.isEmpty && output.removeFirst() == "performNeverAction - completion")
    assert(output.isEmpty) // 'perform never action' should never be output

    // verify user defaults save, second time
    performNeverAction(neverPublisher: saveToUserDefaults)
    assert(!output.isEmpty && output.removeFirst() == "saved to user defaults")
    assert(!output.isEmpty && output.removeFirst() == "performNeverAction - completion")
    assert(output.isEmpty) // 'perform never action' should never be output

    // MARK: - Problem: AnyPublisher<Never, Never> -> AnyPublisher<Void, Never>

    // MARK: Solution 1
    // `|` ➡️ `(Void)|`

    performVoidAction(
    voidPublisher: saveToUserDefaults
    .map { _ in () }
    .append(Just(()))
    .eraseToAnyPublisher())
    assert(output.removeFirst() == "saved to user defaults")
    assert(!output.isEmpty && output.removeFirst() == "performVoidAction - sink")
    assert(!output.isEmpty && output.removeFirst() == "performVoidAction - completion")
    assert(output.isEmpty) // 'perform never action' should never be output"


    // MARK: - Problem: AnyPublisher<Void, Never> -> AnyPublisher<Never, Never>

    // MARK: Solution 2 (Wait)
    // `--->(Void)|` ➡️ `--->|`

    performNeverAction(
    neverPublisher: saveToDatabase.ignoreOutput().eraseToAnyPublisher())
    assert(!output.isEmpty && output.removeFirst() == "saving to database")
    assert(output.isEmpty)
    usleep(halfDelayMicroSeconds + sleepBufferMicroSeconds)
    assert(output.isEmpty)
    usleep(halfDelayMicroSeconds + sleepBufferMicroSeconds)
    assert(!output.isEmpty && output.removeFirst() == "saved to database")
    assert(!output.isEmpty && output.removeFirst() == "performNeverAction - completion")
    assert(output.isEmpty)


    // MARK: Solution 3 (No wait)
    // `--->(Void)|` ➡️ `|`

    performNeverAction(
    neverPublisher: Publishers.Merge(Just<Void>(()), saveToDatabase)
    .first()
    .ignoreOutput()
    .eraseToAnyPublisher())
    assert(!output.isEmpty && output.removeFirst() == "performNeverAction - completion")
    assert(!output.isEmpty && output.removeFirst() == "saving to database")
    assert(output.isEmpty)
    usleep(halfDelayMicroSeconds + sleepBufferMicroSeconds)
    assert(output.isEmpty)
    usleep(halfDelayMicroSeconds + sleepBufferMicroSeconds)
    assert(!output.isEmpty && output.removeFirst() == "saved to database")
    assert(output.isEmpty)

    print("done")
    最后,以下是作为 Publisher 运算符(operator)的解决方案:
    extension Publisher where Output == Never {
    func asVoid() -> AnyPublisher<Void, Failure> {
    self
    .map { _ in () }
    .append(Just(()).setFailureType(to: Failure.self))
    .eraseToAnyPublisher()
    }
    }

    extension Publisher where Output == Void {
    func asNever(completeImmediately: Bool) -> AnyPublisher<Never, Failure> {
    if completeImmediately {
    return Just<Void>(())
    .setFailureType(to: Failure.self)
    .merge(with: self)
    .first()
    .ignoreOutput()
    .eraseToAnyPublisher()
    } else {
    return self
    .ignoreOutput()
    .eraseToAnyPublisher()
    }
    }
    }

    关于swift - 如何转换为/从 AnyPublisher<Void, Never> 和 AnyPublisher<Never, Never>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62856674/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com