Icon credits: Lorc, Delapouite & contributors
Drop one line to use throttle, debounce, and delay with full thread safety: say goodbye to reactive programming like RxSwift and Combine.
import Throttler
debounce {
print("debounce 1 sec")
}
throttle {
print("throttle 1 sec")
}
delay {
print("delay 1 sec")
}
Here's how you can quickly get started.
import SwiftUI
import Throttler
struct ContentView: View {
var body: some View {
VStack {
Button(action: {
for i in 1...10000000 {
throttle {
print("throttle: \(i)")
}
}
}) {
Text("throttle")
}
// Expected Output: Will print "throttle : \(i)" every 1 second (by default)
Button(action: {
delay {
print("delayed 2 seconds")
}
}) {
Text("delay")
}
// Expected Output: Will print "Delayed 2 seconds" after 2 seconds
Button(action: {
for i in 1...10000000 {
debounce {
print("debounce \(i)")
}
}
}) {
Text("debounce")
}
// Expected Output: Will print "debounce" only after the button has not been clicked for 1 second
}
}
}
All of these operations are executed in a thread-safe manner, leveraging the Swift actor model that Throttler utilizes. This guarantees safe access and modification of shared mutable state within the closure of throttle, debounce, and delay functions, regardless of the number of threads involved.
Feed any shared resource into them (debounce, throttle, debounce). The functions will handle everything out of box.
import Foundation
/* a simple thread safe test. */
var a = 0
DispatchQueue.global().async {
for _ in Array(0...10000) {
throttle(.seconds(0.1), by: .ownedActor) {
a+=1
print("throttle1 : \(a)")
}
}
}
DispatchQueue.global().async {
for _ in Array(0...100) {
throttle(.seconds(0.01), by: .ownedActor) {
a+=1
print("throttle2 : \(a)")
}
}
}
DispatchQueue.global().async {
for _ in Array(0...100) {
throttle(.seconds(0.001), by: .ownedActor) {
a+=1
print("throttle3 : \(a)")
}
}
}
DispatchQueue.global().async {
for _ in Array(0...100) {
debounce(.seconds(0.001), by: .ownedActor) {
a+=1
print("debounce1 : \(a)")
}
}
}
//throttle3 : 1
//throttle3 : 2
//throttle3 : 3
//throttle3 : 4
//throttle3 : 5
//throttle3 : 6
//throttle3 : 7
//throttle3 : 8
//throttle3 : 9
//throttle3 : 10
//throttle3 : 11
//debounce1 : 12
//throttle3 : 13
//throttle2 : 14
//throttle1 : 15
//throttle1 : 16
//throttle1 : 17
//throttle1 : 18
//throttle1 : 19
//throttle1 : 20
//throttle1 : 21
//throttle1 : 22
//throttle1 : 23
//throttle1 : 24
//throttle1 : 25
//throttle1 : 26
//throttle1 : 27
/// safe from race condition => ✅
/// safe from data race => ✅
Throttler stands out not just for its advanced features, but also for its incredibly simple-to-use API. Here's how it gives you more, right out of the box, with just a one-liner closure:
/// by default: duration 1 sec and default debounce (not runFirst)
for i in Array(0...100) {
debounce {
print("debounce : \(i)")
}
}
// debounce : 100
/// Expected Output: Executes a first task immediately, then debounce only after 1 second since the last operation.
for i in Array(0...100) {
debounce(.seconds(2), option: .runFirst) {
print("debounce : \(i)")
}
}
// debounce : 1 => 💥
// debounce : 100
/// Throttle and executes once every 1 second.
for i in 1...100000 {
throttle {
print("throttle: \(i)")
}
}
// throttle: 0
// throttle: 41919
// throttle: 86807
/// Guarantees the last call no matter what even after a throttle duration and finished.
for i in 1...100000 {
throttle(option: .ensureLast) {
print("throttle : \(i)")
}
}
// throttle : 0
// throttle : 16363
// throttle : 52307
// throttle : 74711
// throttle : 95747
// throttle : 100000 => 💥
Throttler makes it extremely simple and easy to use advanced features with just a one-liner, unlike RxSwift and Combine where custom implementations are often required.
import RxSwift
let disposeBag = DisposeBag()
Observable.from(1...100000)
.throttle(RxTimeInterval.milliseconds(500), latest: false, scheduler: MainScheduler.instance)
.subscribe(onNext: { i in
print("throttle : \(i)")
})
.disposed(by: disposeBag)
import Combine
var cancellables = Set<AnyCancellable>()
Publishers.Sequence(sequence: 1...100000)
.throttle(for: .milliseconds(500), scheduler: DispatchQueue.main, latest: false)
.sink(receiveValue: { i in
print("throttle : \(i)")
})
.store(in: &cancellables)
throttle {
print("hi")
}
debounce {
print("hi")
}
for i in 1...100000 {
throttle(option: .ensureLast) {
print("throttle : \(i)")
}
}
// throttle : 16363
// throttle : 52307
// throttle : 74711
// throttle : 95747
// throttle : 100000 => 💥
for i in Array(0...100) {
debounce(option: .runFirst) {
print("debounce : \(i)")
}
}
// debounce : 1 => 💥
// debounce : 100
Highly Recommended: While the functions are intentionally designed to run out of the box without specifying an identifier in favor of brevity, it is strongly recommended to provide a custom identifier for
debounce
andthrottle
operations for better control and organization.
// simple and come in handy by default
throttle {
print("simple")
}
// recommended
throttle(identifier: "custom_throttle_id") {
print("This is a recommended way of using throttled.")
}
// simple and come in handy by default
debounce {
print("simple")
}
// recommended
debounce(.seconds(2), identifier: "custom_debounce_id") {
print("This is a recommended way of using debounced.")
}
In favor of new Swift concurrency, this release completely relies on and leverages the new actor model introduced in Swift 5.5 for better performance and safer code execution. The previous versions that used standard Swift methods for task management such as GCD has been completely removed as deprecated to emphasize the use of the actor in a unified way.
(Please be aware that the minimum version requirement has been raised to iOS 16.0, macOS 13.0, watchOS 9.0, and tvOS 16.0.)
As of V2.0.0, struct based way was removed as deprecated in favor of Swift actor type. Please migrate to functions. (throttle, debounce and delay)
iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0
To use the latest V2.0.9 version, add the package to your Package.swift
:
dependencies: [
.package(url: "https://github.com/YourRepo/Throttler.git", .upToNextMajor(from: "2.0.9"))
]
or in Xcode:
https://github.com/boraseoksoon/Throttler.git
Pull requests are warmly welcome as well.
Throttler is released under the MIT license. See LICENSE for details.
link |
Stars: 86 |
Last commit: 1 week ago |
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics