Last active
May 23, 2022 16:21
-
-
Save thecoolwinter/a0666738a783a094c6822344070c2533 to your computer and use it in GitHub Desktop.
DelayedTask.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// The `DelayedTask` waits to execute an action, and will only call it's `onEnd` completion if the | |
/// `action` has been performed. | |
/// | |
/// This allows for situations where you may `await` for a cache item that could take a small amount of time, | |
/// but it could also require a network call. An example of such a situation is below: | |
/// ```swift | |
/// Task { | |
/// self.showLoadingIndicator() | |
/// let data = await ResourceManager.getData() | |
/// self.removeLoadingIndicator() | |
/// } | |
/// ``` | |
/// | |
/// In this situation we may show the loading indicator even though we may not have to show it. That | |
/// `await ResourceManager.getData()` might only take a few ms. | |
/// In that case we would want to wait just a little bit before showing the loading indicator, and when | |
/// we do show it we need a way to remove it afterwards. That's where `DelayedTask` comes in: | |
/// | |
/// ```swift | |
/// Task { | |
/// let task = DelayedTask(duration: 0.2) { | |
/// self.showLoadingIndicator() | |
/// } onEnd: { | |
/// self.removeLoadingIndicator() | |
/// } | |
/// let data = await ResourceManager.getData() | |
/// task.end() | |
/// } | |
/// ``` | |
/// Using this, we'll only show the loading indicator if the `await ResourceManager.getData()` | |
/// method takes more than `0.2` seconds. | |
/// | |
/// This actor also provides a `queue` parameter that defaults to the main queue. This can be changed | |
/// to a different queue if needed, but makes UI changes easier using this actor. | |
class DelayedTask { | |
private let action: (() -> Void) | |
private let onEnd: (() -> Void) | |
private var timer: Timer? = nil | |
private let queue: DispatchQueue | |
/// Initializes the `DelayedTask` and begins wating the given duration immeditately. | |
/// To end the task call the `end()` method on this actor. | |
/// - Parameters: | |
/// - duration: The duration to wait before executing the `action` | |
/// - action: The action to perform after the given duration | |
/// - onEnd: The completion to call when the `end()` method is called. *only executed if the `action` has already been called* | |
/// - queue: The queue to call `action` and `onEnd` on. Defaults to `DispatchQueue.main` | |
init(duration: Double, action: @escaping (() -> Void), onEnd: @escaping (() -> Void), queue: DispatchQueue = .main) async { | |
self.action = action | |
self.onEnd = onEnd | |
self.queue = queue | |
timer = Timer(timeInterval: duration, repeats: false, block: { [weak self] _ in | |
self?.performAction() | |
}) | |
} | |
/// Ends the delayed task. If the timer isn't already finished | |
/// this will not execute the `onEnd` completion. | |
public func end() { | |
if timer != nil { | |
timer?.invalidate() | |
timer = nil | |
} else { | |
queue.async { | |
self.onEnd() | |
} | |
} | |
} | |
private func performAction() { | |
queue.async { | |
self.action() | |
} | |
timer?.invalidate() | |
timer = nil | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment