Skip to content

Instantly share code, notes, and snippets.

@egzonpllana
Last active July 28, 2024 10:09
Show Gist options
  • Save egzonpllana/8857710a23ad8316e6896a7c2be5f48f to your computer and use it in GitHub Desktop.
Save egzonpllana/8857710a23ad8316e6896a7c2be5f48f to your computer and use it in GitHub Desktop.
Service to manage repeated execution of tasks at specified intervals.
import Foundation
/// Protocol defining the behavior of a repeated task service.
protocol RepeatedTaskServiceProtocol {
/// Schedules a repeated task to be executed at specified intervals.
/// - Parameters:
/// - interval: The time interval between executions.
/// - repeatCount: The number of times to repeat the task.
/// - identifier: The optional unique identifier for the task. If nil, an identifier will be auto-generated.
func execute(
every interval: TimeInterval,
repeatCount: Int,
withIdentifier identifier: String?,
task: @Sendable @escaping () -> Void
) throws
/// Stops a task with the specified identifier.
/// - Parameter identifier: The unique identifier for the task.
func stopTask(withIdentifier identifier: String)
/// Stops all currently scheduled tasks.
func stopAllTasks()
/// Stops all tasks without explicit identifiers.
func stopTasksWithoutIdentifiers()
}
extension RepeatedTaskServiceProtocol {
/// Schedules a repeated task to be executed at specified intervals without an identifier.
/// - Parameters:
/// - interval: The time interval between executions.
/// - repeatCount: The number of times to repeat the task.
/// - task: The task to be executed.
func execute(every interval: TimeInterval, repeatCount: Int, task: @Sendable @escaping () -> Void) throws {
try execute(every: interval, repeatCount: repeatCount, withIdentifier: nil, task: task)
}
}
enum RepeatedTaskServiceError: Error {
case identifierAlreadyInUse
}
/// Service to manage repeated execution of tasks at specified intervals.
final class RepeatedTaskService: RepeatedTaskServiceProtocol {
private var tasks: [String: (task: DispatchWorkItem, isAutoGenerated: Bool)] = [:]
private let queue = DispatchQueue(label: "RepeatedTaskServiceQueue")
private var identifierCounter = 0
init() { }
/// Schedules a repeated task to be executed at specified intervals.
/// - Parameters:
/// - interval: The time interval between executions.
/// - repeatCount: The number of times to repeat the task.
/// - task: The task to be executed.
/// - identifier: The optional unique identifier for the task. If nil, an identifier will be auto-generated.
func execute(
every interval: TimeInterval,
repeatCount: Int,
withIdentifier identifier: String?,
task: @Sendable @escaping () -> Void
) throws {
// Check if the identifier is auto-generated
let isAutoGenerated = identifier == nil
// Generate a unique identifier if not provided
let taskIdentifier = identifier ?? generateUniqueIdentifier()
// Check if the identifier is already in use
guard tasks[taskIdentifier] == nil else {
throw RepeatedTaskServiceError.identifierAlreadyInUse
}
// Create a new DispatchWorkItem to execute the task repeatedly
let newTask = DispatchWorkItem {
for _ in 0..<repeatCount {
// Execute the task
task()
// Sleep for the specified interval before the next execution
Thread.sleep(forTimeInterval: interval)
}
}
// Store the task with its identifier
self.tasks[taskIdentifier] = (task: newTask, isAutoGenerated: isAutoGenerated)
// Perform the following actions asynchronously on the queue
queue.async {
// Execute the task on a background queue
DispatchQueue.global(qos: .background).async(execute: newTask)
}
}
/// Stops a task with the specified identifier.
/// - Parameter identifier: The unique identifier for the task.
func stopTask(withIdentifier identifier: String) {
queue.async {
guard let task = self.tasks[identifier] else { return }
task.task.cancel()
self.tasks.removeValue(forKey: identifier)
}
}
/// Stops all currently scheduled tasks.
func stopAllTasks() {
queue.async {
self.tasks.forEach { $0.value.task.cancel() }
self.tasks.removeAll()
}
}
/// Stops all tasks without explicit identifiers.
func stopTasksWithoutIdentifiers() {
queue.async {
self.tasks.filter { $0.value.isAutoGenerated }.forEach { $0.value.task.cancel() }
self.tasks = self.tasks.filter { !$0.value.isAutoGenerated }
}
}
private func generateUniqueIdentifier() -> String {
UUID().uuidString
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment