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