I'm trying to use a UISearchView
to query google places. In doing so, on text change calls for my UISearchBar
, I'm making a request to google places. The problem is I'd rather debounce this call to only request once per 250 ms in order to avoid unnecessary network traffic. I'd rather not write this functionality myself, but I will if I need to.
I found: https://gist.github.com/ShamylZakariya/54ee03228d955f458389 , but I'm not quite sure how to use it:
func debounce( delay:NSTimeInterval, #queue:dispatch_queue_t, action: (()->()) ) -> ()->() {
var lastFireTime:dispatch_time_t = 0
let dispatchDelay = Int64(delay * Double(NSEC_PER_SEC))
return {
lastFireTime = dispatch_time(DISPATCH_TIME_NOW,0)
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
dispatchDelay
),
queue) {
let now = dispatch_time(DISPATCH_TIME_NOW,0)
let when = dispatch_time(lastFireTime, dispatchDelay)
if now >= when {
action()
}
}
}
}
Here is one thing I've tried using the above code:
let searchDebounceInterval: NSTimeInterval = NSTimeInterval(0.25)
func findPlaces() {
// ...
}
func searchBar(searchBar: UISearchBar!, textDidChange searchText: String!) {
debounce(
searchDebounceInterval,
dispatch_get_main_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT),
self.findPlaces
)
}
The resulting error is Cannot invoke function with an argument list of type '(NSTimeInterval, $T5, () -> ())
How do I use this method, or is there a better way to do this in iOS/Swift.
The following is working for me:
Add the below to some file within your project (I maintain a 'SwiftExtensions.swift' file for things like this):
Then set it up in your classes:
You can now call
someClass.doSomething()
repeatedly and it will only save once per second.owenoak's solution works for me. I changed it a little bit to fit my project:
I created a swift file
Dispatcher.swift
:Then I added the following in my UI class:
The key difference from owenoak's anwer is this line:
Without this line, the timer never triggers if the UI loses focus.
Another debounce implementation using class, you may find useful: https://github.com/webadnan/swift-debouncer
Swift 3 version
1. Basic debounce function
2. Parameterized debounce function
Sometimes it's useful to be have the debounce function take a parameter.
3. Example
In the following example you can see, how the debouncing works, using a string parameter to identify the calls.
Note: The
usleep()
function is only used for demo purposes and may not be the most elegant solution for a real app.Result
You always get a callback, when there is an interval of at least 200ms since the last call.
The general solution as provided by the question and built upon in several of the answers, has a logic mistake that causes problems with short debounce thresholds.
Starting with the provided implementation:
Testing with an interval of 30 milliseconds, we can create a relatively trivial example that demonstrates the weakness.
This prints
This is clearly incorrect, because the first call should be debounced. Using a longer debounce threshold (such as 300 milliseconds) will fix the problem. The root of the problem is a false expectation that the value of
DispatchTime.now()
will be equal to thedeadline
passed toasyncAfter(deadline: DispatchTime)
. The intention of the comparisonnow.rawValue >= when.rawValue
is to actually compare the expected deadline to the "most recent" deadline. With small debounce thresholds, the latency ofasyncAfter
becomes a very important problem to think about.It's easy to fix though, and the code can be made more concise on top of it. By carefully choosing when to call
.now()
, and ensuring the comparison of the actual deadline with most recently scheduled deadline, I arrived at this solution. Which is correct for all values ofthreshold
. Pay special attention to #1 and #2 as they are the same syntactically, but will be different if multiple calls are made before the work is dispatched.Utilities
Hopefully, this answer will help someone else that has encountered unexpected behavior with the function currying solution.
Here is a debounce implementation for Swift 3.
https://gist.github.com/bradfol/541c010a6540404eca0f4a5da009c761