mesqueeb/debouncify
```
SwiftUI `onChangeDebounced` View modifier
Suppose you have a function that you want to execute on every keystroke, but debounce it by 300ms to only execute it after the user has stopped typing for a certain amount of time.
Example:
@State private var query: String = ""
func search(_ query: String) async {
print("searching!")
// your search API call logic...
}
var body: some View {
TextField("Search...", text: $query)
.onChangeDebounced(of: query, after: .milliseconds(300)) { _oldValue, newValue in
search(newValue)
}
}Canceling the debounced Task
If you need to cancel the debounced execution of your search function, eg. when the user hits ESC, you can pass a binding with a Task which you can then cancel.
Example:
@State private var query: String = ""
func search(_ query: String) async {
print("searching!")
// your search API call logic...
}
/// The search Task is added by `onChangeDebounced` below
@State private var searchTask: Task<Void, never>? = nil
var body: some View {
TextField("Search...", text: $query)
.onChangeDebounced(of: query, after: .milliseconds(300), task: $searchTask) { _oldValue, newValue in
search(newValue)
}
.onKeyPress(.return) {
searchTask?.cancel()
search(query)
return .handled
}
.onKeyPress(.escape) {
searchTask?.cancel()
return .handled
}
}Swift `Debouncify` Actor
Use Debouncify to wrap a function and it will automatically get debounced each subsequent call.
Example:
// Example debounced function
func search() async {
print("searching!")
// your search API call logic...
}
// Using Debouncify to wrap the function
let searchAfter300ms = Debouncify(call: search, after: .milliseconds(300))
// Usage
Task {
Task { await searchAfter300ms() }
try await Task.sleep(for: .milliseconds(100))
Task { await searchAfter300ms() }
try await Task.sleep(for: .milliseconds(100))
Task { await searchAfter300ms() }
}
// it will only print "searching!" once after 300msCanceling the debounced Task
You can cancel the debounced task by calling the cancel method on the Debouncify instance.
Example:
// Example debounced function
func search() async {}
let searchAfter300ms = Debouncify(by: .milliseconds(300), search)
var task: Task<Any, Any>? = nil
Task { await searchAfter300ms() }
// if the search needs to be cancelled before the Task above finishes
Task { await searchAfter300ms.cancel() }Documentation
See the documentation for more info.
Package Metadata
Repository: mesqueeb/debouncify
Default branch: main
README: README.md