// // DispatchTimerSubscription.swift // Malinka // // Created by Juraldinio on 11/17/22. // Copyright © 2022 NUT.Tech. All rights reserved. // import Foundation import Combine import WalletFoundation /// Configuration for timing struct DispatchTimerConfiguration { /// Queue on which fire let queue: DispatchQueue? /// Firing interval let interval: DispatchTimeInterval /// Allowed delta for system delay let leeway: DispatchTimeInterval /// How many times to fire let times: Subscribers.Demand } struct DispatchTimer: Publisher { typealias Output = DispatchTime typealias Failure = Never let configuration: DispatchTimerConfiguration init(configuration: DispatchTimerConfiguration) { self.configuration = configuration } func receive(subscriber: S) where Failure == S.Failure, Output == S.Input { let subscription = DispatchTimerSubscription(subscriber: subscriber, configuration: configuration) subscriber.receive(subscription: subscription) } } private final class DispatchTimerSubscription: Subscription where S.Input == DispatchTime { let configuration: DispatchTimerConfiguration var times: Subscribers.Demand var requested: Subscribers.Demand = .none var source: DispatchSourceTimer? var subscriber: S? init(subscriber: S, configuration: DispatchTimerConfiguration) { self.subscriber = subscriber self.configuration = configuration self.times = configuration.times } func request(_ demand: Subscribers.Demand) { guard self.times > .none else { self.subscriber?.receive(completion: .finished) return } self.requested += demand if !self.source.isExist, self.requested > .none { let source = DispatchSource.makeTimerSource(queue: configuration.queue) source.schedule(deadline: .now() + configuration.interval, repeating: configuration.interval, leeway: configuration.leeway) source.setEventHandler { [weak self] in guard let self = self, self.requested > .none else { return } self.requested -= .max(1) self.times -= .max(1) _ = self.subscriber?.receive(.now()) if self.times == .none { self.subscriber?.receive(completion: .finished) } } self.source = source source.activate() } } func cancel() { self.source = nil self.subscriber = nil } }