Add search and search suggestions to a SwiftUI app with .searchable

This article walks through a compact iOS 15 SwiftUI pattern: add a search field with .searchable, filter a list from bound state, then use the modifier's suggestion closure and .searchCompletion to help the user finish the query faster.

SwiftUI searchable demo showing a list and search interface

This post is about the smallest useful version of SwiftUI search: bind the query to state, filter the list, then improve the experience with suggestions.

This article uses a simple list of cat records as the example data set. That keeps the point clear: the interesting part is not the model, but how .searchable attaches a search field to the list and how the suggestion closure can reduce typing friction.

The article also notes that this search suggestion behavior may be limited to iOS 15 and later, which was the new API surface at the time.

Code Link This article links its final sample code as a gist: mszpro/e22105fa05485b2e8025e9cb2a8eaa25.

The demo begins with a plain SwiftUI view backed by a small array of cat information and one state property for the search text.

The first important piece is just the bound query string:

@State private var searchText: String = ""

The sample data in this article is a list of cat names, colors, and descriptions. That is enough to show search behavior without introducing database or API code.

Overview image from the searchable SwiftUI article
The article uses a lightweight cat list to keep the searchable example focused.

The first usable version is simple: attach .searchable to the list, then filter the items from searchText.

The modifier itself is straightforward:

.searchable(
    text: $searchText,
    placement: .navigationBarDrawer,
    prompt: "Search from cat names"
)

Then the list content is filtered based on the typed text. In this article, an empty query returns every item, while a non-empty query checks for an exact cat-name match:

List(catInfos.filter({ catInfo in
    if self.searchText.isEmpty {
        return true
    }
    return catInfo.catName == self.searchText
})) { catInfo in
    VStack(alignment: .leading) {
        Text(catInfo.catName)
            .font(.headline)
            .foregroundColor(catInfo.catColor)
        Text(catInfo.catDescription)
    }
}

That works, but it produces an obvious UX issue: the user has to type the full cat name before the row appears. The article uses that limitation as the reason to add suggestions next.

Basic searchable list requiring an exact cat name
The exact-match version works, but it is too strict for comfortable typing.

The improvement is to use the searchable suggestion closure so the UI can offer matching names before the user finishes typing.

SwiftUI lets you add a trailing closure to .searchable and build suggestion content there:

.searchable(
    text: $searchText,
    placement: .navigationBarDrawer,
    prompt: "Search from cat names"
) {
    let matchedItems = self.catInfos.filter({ catInfo in
        return catInfo.catName.contains(self.searchText)
    })

    if matchedItems.isEmpty {
        Text("No suggestions found.")
    }

    ForEach(matchedItems) { catInfo in
        Text("Are you looking for \(catInfo.catName)?")
            .searchCompletion(catInfo.catName)
    }
}

This article keeps the matching logic intentionally small by using contains. In a real app, that same block could call into a database, an API, or a local search index instead.

The key API detail is .searchCompletion. That modifier turns each suggestion row into a value the user can tap to populate the search field directly.

Search suggestions appearing beneath a SwiftUI searchable field
Suggestions remove the need to type the entire cat name before getting a useful result.

This article ends by linking the full sample rather than expanding every line inline.

If you want the exact complete version from the article, use the linked gist: mszpro/e22105fa05485b2e8025e9cb2a8eaa25.

Conceptually, the whole implementation is just one list view plus three moving parts: the bound search text, the filtered list data, and the suggestion rows returned from the searchable closure.

This is a good example of how small SwiftUI modifiers become much more valuable once you pair them with the right interaction pattern.

.searchable by itself gives you a search field. The suggestion closure and .searchCompletion are what make it feel faster and less brittle for real users.

If your SwiftUI list search still depends on exact full-string entry, this is a clean next step.