Icon credits: Lorc, Delapouite & contributors
Throttler
Throttler is a library that throttles unnecessarily repeated and
massive inputs until the last in a single drop of one liner API.
How to use
Just drop it.
import Throttler
for i in 1...1000 {
Throttler.go {
print("go! > \(i)")
}
}
// go! > 1000
Use case
While it is originally developed to solve the problem where vast number of user typing input
involving CPU intensive tasks have be to performed repeatedly and constantly
on HLVM,
A common problem that Throttler can solve is
a user taps a button that requests asynchronous network call a massive number of times
within few seconds.
With Throttler,
import UIKit
import Throttler
class ViewController: UIViewController {
@IBOutlet var button: UIButton!
var index = 0
/********
Assuming your users will tap the button, and
request asyncronous network call 10 times(maybe more?) in a row within very short time nonstop.
*********/
@IBAction func click(_ sender: Any) {
print("click1!")
Throttler.go {
// Imaging this is a time-consuming and resource-heavy task that takes an unknown amount of time!
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
self.index += 1
print("click1 : \(self.index) : \(String(data: data, encoding: .utf8)!)")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Output:
click1!
click1!
click1!
click1!
click1!
click1!
click1!
click1!
click1!
click1!
2021-02-20 23:16:50.255273-0500 iOSThrottleTest[24776:813744]
click1 : 1 : {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Without Throttler
class ViewController: UIViewController {
@IBOutlet var button: UIButton!
var index = 0
/********
Assuming your users will tap the button, and
request asyncronous network call 10 times(maybe more?) in a row within very short time nonstop.
*********/
@IBAction func click(_ sender: Any) {
print("click1!")
// Imaging this is a time-consuming and resource-heavy task that takes an unknown amount of time!
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
self.index += 1
print("click1 : \(self.index) : \(String(data: data, encoding: .utf8)!)")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
if you don't use Throttler, Output is as follows:
/*
click1!
2021-02-20 23:16:50.255273-0500 iOSThrottleTest[24776:813744]
click1 : 1 : {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
click1!
2021-02-20 23:16:50.255273-0500 iOSThrottleTest[24776:813744]
click1 : 1 : {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
click1!
2021-02-20 23:16:50.255273-0500 iOSThrottleTest[24776:813744]
click1 : 1 : {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
click1!
2021-02-20 23:16:50.255273-0500 iOSThrottleTest[24776:813744]
click1 : 1 : {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
.......
......
.....
...
..
.
Your server will start to cry!
😂😂😂
*/
Fundamentally, Throttler can be used to solve various type of problems
related to any repeated and continuous input issue
usually iOS software engineers usually want to avoid.
It is a common situation I can meet in my daily work.
Think about how many lines you need to write without Throttler
to solve these repeatedly tedious and error-prone task!
Why made?
Is it only me who always realized later on iOS I did not validate continuous and repeated input of users (who rapidly bash buttons like tap tap tap, click click click really in a 10 times in a row in few seconds) requesting pretty heavy HTTP request or some resource consuming task? and QA told me please it needs to be controlled on front-end in the first place.
After that, I always repeatedly used to implement this task using DispatchWorkItem or Combine, Timer with isUserInteractionEnabled flags or
even worse, UIApplication.shared.beginIgnoringInteractionEvents()
UIApplication.shared.endIgnoringInteractionEvents() things like that... ( I know it should be only used when you have really no time under serious pressure)
Again, this time while doing my own project, I met this issue again.
So, I made up my mind to build my own yesterday.
Advantages Versus Combine, RxSwift Throttle and Debounce
- Concise API, one liner, no brainer
- DispatchWorkItem does the job here. It can cancel http request not initiated out of box.
- Backward compatibility. Combine needs iOS 13 / macOS Catalina and its new runtime to work. There is no backward compatibility to earlier versions of their operating systems planned. (currently we are living 2021 though...;)
Requirements
- Swift 5.3+
Installation
Swift Package Manager
- File > Swift Packages > Add Package Dependency
- Add
https://github.com/boraseoksoon/Throttler.git
- Click Next.
- Done :)
Contact
Pull requests are warmly welcome as well.
License
Throttler is released under the MIT license. See LICENSE for details.