gh-Dimillian-Skills/swiftui-ui-patterns/references/searchable.md
Thomas Ricouard 70a15d08db Add SwiftUI UI patterns skill and references
Introduces the 'swiftui-ui-patterns' skill to docs/skills.json, providing best practices and example-driven guidance for building SwiftUI views and components. Adds SKILL.md and a comprehensive set of reference files covering TabView, NavigationStack, sheets, forms, controls, grids, overlays, haptics, focus handling, media, matched transitions, split views, and more.
2026-01-04 18:26:56 +01:00

1.7 KiB
Raw Permalink Blame History

Searchable

Intent

Use searchable to add native search UI with optional scopes and async results.

Core patterns

  • Bind searchable(text:) to local state.
  • Use .searchScopes for multiple search modes.
  • Use .task(id: searchQuery) or debounced tasks to avoid overfetching.
  • Show placeholders or progress states while results load.

Example: searchable with scopes

@MainActor
struct ExploreView: View {
  @State private var searchQuery = ""
  @State private var searchScope: SearchScope = .all
  @State private var isSearching = false
  @State private var results: [SearchResult] = []

  var body: some View {
    List {
      if isSearching {
        ProgressView()
      } else {
        ForEach(results) { result in
          SearchRow(result: result)
        }
      }
    }
    .searchable(
      text: $searchQuery,
      placement: .navigationBarDrawer(displayMode: .always),
      prompt: Text("Search")
    )
    .searchScopes($searchScope) {
      ForEach(SearchScope.allCases, id: \.self) { scope in
        Text(scope.title)
      }
    }
    .task(id: searchQuery) {
      await runSearch()
    }
  }

  private func runSearch() async {
    guard !searchQuery.isEmpty else {
      results = []
      return
    }
    isSearching = true
    defer { isSearching = false }
    try? await Task.sleep(for: .milliseconds(250))
    results = await fetchResults(query: searchQuery, scope: searchScope)
  }
}

Design choices to keep

  • Show a placeholder when search is empty or has no results.
  • Debounce input to avoid spamming the network.
  • Keep search state local to the view.

Pitfalls

  • Avoid running searches for empty strings.
  • Dont block the main thread during fetch.