您如何 运行 一个 Swift 合并发布者一段时间?

How do you run a Swift Combine Publisher for a certain amount of time?

我有一个publisher当sink,扫描一个wifi列表。我只想扫描大约 10 秒然后停止。

有没有办法在发布者调用链中执行此操作?

这个运算符可以解决问题。

import PlaygroundSupport
import Foundation
import Combine

let page = PlaygroundPage.current
page.needsIndefiniteExecution = true

extension Publisher {
    func stopAfter<S>(_ interval: S.SchedulerTimeType.Stride, tolerance: S.SchedulerTimeType.Stride? = nil, scheduler: S, options: S.SchedulerOptions? = nil) -> AnyPublisher<Output, Failure> where S: Scheduler {
        prefix(untilOutputFrom: Just(()).delay(for: interval, tolerance: tolerance, scheduler: scheduler, options: nil))
            .eraseToAnyPublisher()
    }
}

let source = Timer.publish(every: 1, tolerance: nil, on: RunLoop.main, in: .default, options: nil)
    .autoconnect()
    .eraseToAnyPublisher()

let cancellable = source
    .stopAfter(10, scheduler: DispatchQueue.main)
    .sink(receiveValue: { print([=10=]) })

您可以使用 timeout() 运算符:

Terminates publishing if the upstream publisher exceeds the specified time interval without producing an element.

wifiScannerPublisher
    .timeout(.seconds(waitTime), scheduler: DispatchQueue.main, options: nil, customError:nil)
    .sink(
        receiveCompletion: { print("completion: \([=10=]), at: \(Date())") },
        receiveValue: { print("wifi: \([=10=])") }
     )

但是,如果您的发布者一直定期发出事件,而您只是想在一段时间后停止它,那么 答案可能是可行的方法。

尽管如此,我将通过使用 timeout()scan():

Publisher 扩展自行添加一个解决方案
extension Publisher {
    func stopAfter(_ interval: TimeInterval) -> AnyPublisher<Output, Failure> {
        self
            .timeout(.seconds(interval), scheduler: DispatchQueue.main)
            .scan((Date()+interval, nil)) { ([=11=].0, ) }
            .prefix(while: { Date() < [=11=].0 })
            .map { [=11=].1! }
            .eraseToAnyPublisher()
    }
}

以上发布者会携带超时日期,一旦到达该日期,它就会停止。需要 map 来丢弃物品中携带的额外 Date

用法:

wifiListPublisher.stopAfter(10)
    .sink(...)