History
Loading...
Loading...
September 10, 2025
.searchable() modifier with @State to add native search functionality to any view. The search field automatically integrates with navigation bars and provides platform-appropriate styling across iOS, macOS, and other platforms.This pattern separates immediate search input from actual filtering logic using a debounced approach. The search text updates immediately for responsive UI, while filtering operations are delayed by 300ms to prevent excessive computation.
struct ContentView: View {
@State private var searchText = ""
@State private var debouncedSearchText = ""
@State private var searchTask: Task<Void, Never>?
let items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]
var filteredItems: [String] {
debouncedSearchText.isEmpty ? items :
items.filter { $0.localizedCaseInsensitiveContains(debouncedSearchText) }
}
var body: some View {
NavigationView {
List(filteredItems, id: \.self) { item in
Text(item)
}
.navigationTitle("Fruits")
.searchable(text: $searchText, prompt: "Search fruits")
.onChange(of: searchText) { _, newValue in
searchTask?.cancel()
searchTask = Task {
try? await Task.sleep(nanoseconds: 300_000_000)
if !Task.isCancelled {
debouncedSearchText = newValue
}
}
}
}
}
}Without debouncing, every keystroke triggers expensive filtering operations, leading to poor performance and unnecessary battery drain. This approach provides smooth user experience while maintaining efficiency, especially important for large datasets or complex search operations.