How to perform throttling in Swift, Debounce.

What is Throttling all about, it’s a process responsible for regulating the rate at which application processing is conducted (doesn’t matter statically or dynamically). Wikipedia

A very common use in iOS, when you have a search bar which takes input from user and sends off a request for search results every time. For a long query it may cause lot of requests to be sent on server to process (performance will be different for slow vs fast typing user & also depends on internet connection).

Another use case that I faced today is to log an impression when user sees the TableViewCell. I found and interesting example on Github. I created an extension for this to make use of it everywhere.

Create a protocol.

protocol Throttable {
    func perform(with delay: TimeInterval,
                 in queue: DispatchQueue,
                 block completion: @escaping () -> Void) -> () -> Void
}

Provide the default implementation using the extension.

extension Throttable {
    func perform(with delay: TimeInterval,
                 in queue: DispatchQueue = DispatchQueue.main,
                 block completion: @escaping () -> Void) -> () -> Void {
        
        var workItem: DispatchWorkItem?
        
        return {
            workItem?.cancel()
            workItem = DispatchWorkItem(block: completion)
            queue.asyncAfter(deadline: .now() + delay, execute: workItem!)
        }
    }
}

Responsibility of this function is just to return a function, when you call the returned function.

  • It cancels the exiting workItem if exists (magical Optional).
  • Creates a new DispatchWorkItem with the completion block.
  • And the queue calls the workItem with given delay.

And that’s how it works, Just conforms the Throttable protocol wherever you want.

class UserInputHandler: Throttable {
    let triggerAPI = perform(with: 1.0) {
        print("Fetch Search Result")
    }
    
    func didReceiveInput() {
        triggerAPI()
    }
}

Here is an easy example to integration it with UIViewController

class SearchViewController: UIViewController, Throttable {
    @IBOutlet weak var searchTextField: UITextField!
    
    lazy var triggerAPI = perform(with: 1.0) { [weak self] in
        
        guard let searchText = self?.searchTextField.text, !searchText.isEmpty else {
            return
        }
        
        print("API Request to fetch result for \(searchText)")
    }
}

extension SearchViewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        triggerAPI()
        return true
    }
}

Thanks for reading
Find more Swift Tips and Tricks on Github.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.