---
# System prepended metadata

title: Countdown Timer Publisher (Swift5.2)
tags: [Swift]

---

###### 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)


