From baba499a30fb97a35609ae69ea272a265d477483 Mon Sep 17 00:00:00 2001 From: Thibault Wittemberg Date: Thu, 13 Oct 2022 18:48:05 +0200 Subject: [PATCH] project: add task extensions --- CHANGELOG.md | 4 ++++ README.md | 11 +++++++++++ Sources/Debouncer.swift | 16 +++++++++++++++- Sources/SwiftUI/RegulatedButtonStyle.swift | 2 +- Sources/Throttler.swift | 18 +++++++++++++++++- Tests/DebouncerTests.swift | 2 +- Tests/ThrottlerTests.swift | 6 +++--- 7 files changed, 52 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60471f9..463549b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +**v0.2.0:** + +- add static factory methods on `Task` + **v0.1.0:** - Debouncer diff --git a/README.md b/README.md index 37a8b4f..bd1f30e 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,17 @@ **Regulate** is entirely backed by Swift concurrency and limits the number of created `Tasks` to the minimum. +```swift +let regulator = Task.debounce(dueTime: .milliseconds(200)) { (value: Int) in + print(value) +} + +// the created `regulator` can be used across `Tasks` and each call to `regulator.push(x)` +// will feed the regulation system + +// the execution of the provided closure will be debounced and executed 200ms after the last call to `push(x)` +``` + **Regulate** also provides SwiftUI helpers to regulate buttons and bindings out of the box. You can give a look at the [Sample app](./Sample). diff --git a/Sources/Debouncer.swift b/Sources/Debouncer.swift index 439daa1..f5e3b29 100644 --- a/Sources/Debouncer.swift +++ b/Sources/Debouncer.swift @@ -7,6 +7,20 @@ import Foundation +public extension Task where Failure == Never { + /// Creates a `Regulator` that executes an output only after a specified time interval elapses between events + /// - Parameters: + /// - dueTime: the time the Debouncer should wait before executing the output + /// - output: the block to execute once the regulation is done + /// - Returns: the debounced regulator + static func debounce( + dueTime: DispatchTimeInterval, + output: @Sendable @escaping (Success) async -> Void + ) -> some Regulator { + Debouncer(dueTime: dueTime, output: output) + } +} + /// Executes an output only after a specified time interval elapses between events /// /// ```swift @@ -79,7 +93,7 @@ public final class Debouncer: @unchecked Sendable, ObservableObject, Regu /// A Regulator that executes the output only after a specified time interval elapses between events /// - Parameters: /// - dueTime: the time the Debouncer should wait before executing the output - /// - output: the block to execute once the regulationis done + /// - output: the block to execute once the regulation is done public init( dueTime: DispatchTimeInterval, output: (@Sendable (Value) async -> Void)? = nil diff --git a/Sources/SwiftUI/RegulatedButtonStyle.swift b/Sources/SwiftUI/RegulatedButtonStyle.swift index 93fc903..b14f9bb 100644 --- a/Sources/SwiftUI/RegulatedButtonStyle.swift +++ b/Sources/SwiftUI/RegulatedButtonStyle.swift @@ -20,7 +20,7 @@ public struct RegulatedButtonStyle>: PrimitiveButtonStyle { regulator.dueTime = self.dueTime regulator.output = { _ in configuration.trigger() } - if #available(iOS 15.0, *) { + if #available(iOS 15.0, macOS 12.0, *) { return Button(role: configuration.role) { regulator.push(()) } label: { diff --git a/Sources/Throttler.swift b/Sources/Throttler.swift index 9c3778f..6ddeddf 100644 --- a/Sources/Throttler.swift +++ b/Sources/Throttler.swift @@ -7,6 +7,22 @@ import Foundation +public extension Task where Failure == Never { + /// Creates a `Regulator` that executes the output with either the most-recent or first element + /// pushed in the Throttler in the specified time interval + /// - dueTime: the interval at which to find and emit either the most recent or the first element + /// - latest: true if output should be called with the most-recent element, false otherwise + /// - output: the block to execute once the regulation is done + /// - Returns: the throttled regulator + static func throttle( + dueTime: DispatchTimeInterval, + latest: Bool = true, + output: @Sendable @escaping (Success) async -> Void + ) -> some Regulator { + Throttler(dueTime: dueTime, latest: latest, output: output) + } +} + /// Executes the output with either the most-recent or first element pushed in the Throttler in the specified time interval /// /// ```swift @@ -84,7 +100,7 @@ public final class Throttler: @unchecked Sendable, ObservableObject, Regu /// - Parameters: /// - dueTime: the interval at which to find and emit either the most recent or the first element /// - latest: true if output should be called with the most-recent element, false otherwise - /// - output: the block to execute once the regulationis done + /// - output: the block to execute once the regulation is done public init( dueTime: DispatchTimeInterval, latest: Bool = true, diff --git a/Tests/DebouncerTests.swift b/Tests/DebouncerTests.swift index f92c577..4c7dbf0 100644 --- a/Tests/DebouncerTests.swift +++ b/Tests/DebouncerTests.swift @@ -13,7 +13,7 @@ final class DebouncerTests: XCTestCase { let hasDebounced = expectation(description: "Has debounced a value") let spy = Spy() - let sut = Debouncer(dueTime: .milliseconds(200)) { value in + let sut = Task.debounce(dueTime: .milliseconds(200)) { value in await spy.push(value) hasDebounced.fulfill() } diff --git a/Tests/ThrottlerTests.swift b/Tests/ThrottlerTests.swift index 5875b32..eb78a31 100644 --- a/Tests/ThrottlerTests.swift +++ b/Tests/ThrottlerTests.swift @@ -15,7 +15,7 @@ final class ThrottlerTests: XCTestCase { let spy = Spy() - let sut = Throttler(dueTime: .milliseconds(100), latest: false) { value in + let sut = Task.throttle(dueTime: .milliseconds(100), latest: false) { value in await spy.push(value) hasThrottledTwoValues.fulfill() } @@ -39,7 +39,7 @@ final class ThrottlerTests: XCTestCase { let spy = Spy() - let sut = Throttler(dueTime: .milliseconds(100), latest: true) { value in + let sut = Task.throttle(dueTime: .milliseconds(100), latest: true) { value in await spy.push(value) hasThrottledTwoValues.fulfill() } @@ -63,7 +63,7 @@ final class ThrottlerTests: XCTestCase { let spy = Spy() - let sut = Throttler(dueTime: .milliseconds(100), latest: true) { value in + let sut = Task.throttle(dueTime: .milliseconds(100), latest: true) { value in await spy.push(value) hasThrottledTwoValues.fulfill() }