In the past we had to set the countdown by Timer.TimePublisher.
We maybe coding to like bellow.
let timer = Timer.publish(every: 0.01, on: .main, in: .common)
.autoconnect()
.map { _ in return 0.01 }
.scan(10, -)
.share()
timer
.map { dateFormatterTransform($0) }
.assign(to: \.text, on: label)
.store(in: &bag)
timer
.map { $0 <= 0 }
.filter { $0}
.sink(receiveValue: toDoCancel)
.store(in: &bag)
It works perfect but not clean on really.
A protocol representing the connection of a subscriber to a publisher.
I prefer to call it flow producer.
The source of the whole resource.
Building a Countdown Subscription I build a custom subscription for countdown feature. It has two important properties, everyTimeInterval and duration, and Output type is TimeInterval, will call finished in time out.
extension Timer {
private class CountdownSubscription<SubscriberType: Subscriber>: Subscription where SubscriberType.Input == TimeInterval{
private var subscriber: SubscriberType?
private let duration: TimeInterval
private let everyTimeInterval: TimeInterval
private var timeTask: AnyCancellable?
private var bag: Set<AnyCancellable> = []
init(every: TimeInterval = 0.01, _ duration: TimeInterval, _ subscriber: SubscriberType) {
self.duration = duration
self.subscriber = subscriber
self.everyTimeInterval = every
start()
}
func request(_ demand: Subscribers.Demand) {}
func cancel() {
subscriber?.receive(completion: .finished)
subscriber = nil
}
private func start() {
let cancel: (Bool)-> () = { [weak self] bool in
guard bool else { return }
self?.cancel()
}
let every = everyTimeInterval
let task = Timer.publish(every: every, on: .main, in: .common)
.autoconnect()
.map { _ in return every }
.scan(duration, -)
.share()
task.sink(receiveValue: { [weak self] in
_ = self?.subscriber?.receive($0 > 0 ? $0 : 0)
}).store(in: &bag)
task.map { $0 <= 0 }.sink(receiveValue: cancel).store(in: &bag)
}
}
}
Declares that a type can transmit a sequence of values over time..
A simple job in today's feature. Building a Countdown Publisher It just a simple case help caller to observe and bind.
struct CountdownPublisher: Combine.Publisher {
typealias Output = TimeInterval
typealias Failure = Never
let duration: TimeInterval
func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, TimeInterval == S.Input {
let scr = CountdownSubscription(duration, subscriber)
subscriber.receive(subscription: scr)
}
}
Create a Timer extension to build our Countdown Timer Publisher.
extension Timer {
static func counrdownPublish(_ duration: TimeInterval)-> CountdownPublisher {
return CountdownPublisher(duration: duration)
}
}
Test
It work perfectly.
Timer.counrdownPublish(10)
.print()
.sink(receiveValue: { _ in })
.store(in: &bag)