I have a publisher when the sink, scans for a list of wifi. I only want to scan for about 10 seconds and stop.
Is there a way to do this within the publisher chain of calls?
To stop the timer, you have two choices as well: Cancel the subscriber. If you called connect() on the timer publisher, that call returned a Cancellable object. If you kept a reference to that object, you can send cancel() to it.
A PassthroughSubject broadcasts elements to downstream subscribers and provides a convenient way to adapt existing imperative code to Combine. As the name suggests, this type of subject only passes through values meaning that it does not capture any state and will drop values if there aren't any subscribers set.
Combines elements from this publisher with those from another publisher of the same type, delivering an interleaved sequence of elements. func merge<P>(with: P) -> Publishers. Merge<Self, P> Combines elements from this publisher with those from another publisher, delivering an interleaved sequence of elements.
This operator will do the trick.
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($0) })
You can use the timeout() operator:
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: \($0), at: \(Date())") },
receiveValue: { print("wifi: \($0)") }
)
If, however your publisher keeps regularly emitting events, and you just want to stop it after an amount of time passes, then Daniel's answer is probably the way to go.
Nonetheless, I'll add a solution on my own, via a Publisher
extension that uses timeout()
and scan()
:
extension Publisher {
func stopAfter(_ interval: TimeInterval) -> AnyPublisher<Output, Failure> {
self
.timeout(.seconds(interval), scheduler: DispatchQueue.main)
.scan((Date()+interval, nil)) { ($0.0, $1) }
.prefix(while: { Date() < $0.0 })
.map { $0.1! }
.eraseToAnyPublisher()
}
}
The above publisher will carry the timeout date, and once that date is reached, it will stop. The map
is needed to discard the extra Date
carried along the items.
Usage:
wifiListPublisher.stopAfter(10)
.sink(...)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With