###### tags: `Swift`
# Countdown Timer Publisher (Swift5.2)
In the past we had to set the countdown by Timer.TimePublisher.
We maybe coding to like bellow.
```swift=
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.
### Let's build our publisher for this feature.
[***`Subscription`***](https://developer.apple.com/documentation/combine/subscription)
> 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.
```swift=
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)
}
}
}
```
[***`Publisher`***](https://developer.apple.com/documentation/combine/publisher)
> 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.
```swift=
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)
}
}
```
## Final
Create a Timer extension to build our Countdown Timer Publisher.
```swift=
extension Timer {
static func counrdownPublish(_ duration: TimeInterval)-> CountdownPublisher {
return CountdownPublisher(duration: duration)
}
}
```
## ***Test***
It work perfectly.
```swift=
Timer.counrdownPublish(10)
.print()
.sink(receiveValue: { _ in })
.store(in: &bag)
```
[***`Full version`***](https://gist.github.com/woodycatliu/6565d8e50dcaac12048d3d64fcb31a44#file-timerpublisherextension-swift)