4

Is there a simple way of implementing the Throttle feature in Reactive programming without having to use RxSwift or similar frameworks.

I have a textField delegate method that I would like not to fire every time a character is inserted/deleted.

How to do that using vanilla Foundation?

ielyamani
  • 17,807
  • 10
  • 55
  • 90
  • 1
    The delegate will fire every time. In your delegate method you can start a timer. If the delegate fires again before the timer goes off, restart the timer. When the timer goes off, do whatever you need to. – Paulw11 Apr 10 '17 at 12:11

2 Answers2

11

Yes it is possible to achieve.

But first lets answer small question what is Throttling?

In software, a throttling process, or a throttling controller as it is sometimes called, is a process responsible for regulating the rate at which application processing is conducted, either statically or dynamically.

Example of the Throttling function in the Swift.

In case that you have describe with delegate method you will have issue that delegate method will be called each time. So I will write short example how it impossible to do in the case you describe.

class ViewController: UIViewController {
    
    var timer: Timer?
    
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textField.delegate = self
    }
    
    @objc func validate() {
        print("Validate is called")
    }
}

extension ViewController: UITextFieldDelegate {
    
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        
        timer?.invalidate()
        
        timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(self.validate), userInfo: nil, repeats: false);
        
        return true
    }

}
Marián Černý
  • 15,096
  • 4
  • 70
  • 83
Oleg Gordiichuk
  • 15,240
  • 7
  • 60
  • 100
  • 1
    `timer?.invalidate()` is a bit simpler. – Sulthan Apr 10 '17 at 12:53
  • I think this will do for now. The problem with using just a timer is: with every new timer the selector is executed **without** cancelling the previous call. and this would defeat the whole point. And there would be no guarantee that the calls to the selector are in the assumed order. – ielyamani Apr 10 '17 at 12:54
  • @Carpsen90 You don't actually need them to be in a particular order. You just need to ignore all the previous results. The way this should be done depends on your API. I am usually passing some context along the request with the text inside the context. When the request completes, I can compare that the current text is the same that the one we requested. – Sulthan Apr 10 '17 at 12:57
  • In other words, the selector should be cancelable. I'll have a look. – ielyamani Apr 10 '17 at 13:02
  • Isn't this a debounce function? A throttle function, wouldn't always trigger `0.5` from the last textField input. Instead it would always trigger at `0.5` regardless of whether the user has input new characters or not. – Shaheen Ghiassy Jun 29 '21 at 17:44
2
  • disclaimer: I am a writer.

Throttler can be the right katana to make you happy.

You can do debounce and throttle without going reactive programming using Throttler like,

import Throttler

// advanced debounce, running a first task immediately before initiating debounce.

for i in 1...1000 {
    Throttler.debounce {
        print("debounce! > \(i)")
    }
}

// debounce! > 1
// debounce! > 1000


// equivalent to debounce of Combine, RxSwift.

for i in 1...1000 {
    Throttler.debounce(shouldRunImmediately: false) {
        print("debounce! > \(i)")
    }
}

// debounce! > 1000

Throttler also can do advanced debounce, running a first event immediately before initiating debounce that Combine and RxSwift don't have by default.

You could, but you may need a complex implementation yourself for that.

boraseoksoon
  • 2,164
  • 1
  • 20
  • 25