Skip to content

Instantly share code, notes, and snippets.

@satishVekariya
Created June 4, 2023 22:58
Show Gist options
  • Save satishVekariya/ced1f260484dcbe16acef58998631920 to your computer and use it in GitHub Desktop.
Save satishVekariya/ced1f260484dcbe16acef58998631920 to your computer and use it in GitHub Desktop.
import Combine
import Foundation
// MARK: - Polling
/// A thread safe polling(repeat task over given time) handler object
public actor Polling {
public typealias TaskItem = () async -> Void
public typealias PollingTask = Task<Void, Never>
typealias PollingSubject = CurrentValueSubject<Int, Never>
private var pollingTask: PollingTask?
private var pollingSubject = PollingSubject(0)
public init() {}
deinit {
pollingTask?.cancel()
}
/// Start polling
/// - Parameters:
/// - interval: Given time interval in seconds
/// - taskItem: A async closure task that will repeat over time
public func start(_ interval: TimeInterval, taskItem: @escaping TaskItem) {
/// Cancel previous task
pollingTask?.cancel()
pollingTask = Task { [weak self] in
/// One time perform on start
await taskItem()
guard let self, Task.isCancelled == false else {
return
}
/// Create async sequence
let asyncSequence = await pollingSubject
.debounce(for: .seconds(interval), scheduler: DispatchQueue.global())
.values
/// Loop over async sequence
for await _ in asyncSequence {
/// Check task is canceled or not
guard Task.isCancelled == false else {
return
}
/// Perform given task
await taskItem()
/// Trigger next iteration
await pollingSubject.send(pollingSubject.value + 1)
}
}
}
/// Stop polling
public func stop() {
pollingTask?.cancel()
}
}
public extension Polling {
/// Convenient way to start polling.
/// - Parameters:
/// - interval: Given time interval
/// - taskItem: Given asynchronous task
/// - Returns: An object of `CancelablePolling` you should hold this in memory as long as you interested in polling.
static func startWith(_ interval: TimeInterval, taskItem: @escaping TaskItem) -> CancelablePolling {
let task = Task {
let polling = Polling()
await polling.start(interval, taskItem: taskItem)
return polling
}
return CancelablePolling(polling: task)
}
}
// MARK: - CancelablePolling
public struct CancelablePolling: Cancellable {
let polling: Task<Polling, Never>
fileprivate init(polling: Task<Polling, Never>) {
self.polling = polling
}
public func cancel() {
Task {
await polling.value.stop()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment