Files
raspberry/iOS/Wallet/Sources/Combine/DispatchTimerSubscription.swift

103 lines
2.7 KiB
Swift

//
// 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<S: Subscriber>(subscriber: S) where Failure == S.Failure, Output == S.Input {
let subscription = DispatchTimerSubscription(subscriber: subscriber,
configuration: configuration)
subscriber.receive(subscription: subscription)
}
}
private final class DispatchTimerSubscription<S: Subscriber>: 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
}
}