Contents

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 300ms

Canceling 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