Adding a search bar interface for data filtering in SwiftUI

In a previous article, I wrote about UISearchController and how to display a filtrable content in UIKit.

Now with SwiftUI modifiers, this task has become way easier and with just few lines of code you can have your search bar with data filtering up and running.

To implement this in your app, start by creating a new SwiftUI project in Xcode and open the ContentView.swift file

Add the following variables at the start of the ContentView struct:

    @State private var searchText: String = ""
    private var cities = ["Amsterdam",
                          "Bangkok",
                          "Cairo",
                          "Dubai",
                          "Edinburgh",
                          "Florence",
                          "Geneva",
                          "Helsinki",
                          "Istanbul",
                          "Jakarta",
                          "Kuala Lumpur",
                          "Lisbon",
                          "Madrid",
                          "Nairobi",
                          "Oslo",
                          "Paris",
                          "Quito",
                          "Rome",
                          "Sydney",
                          "Tokyo",
                          "Ulaanbaatar",
                          "Vienna",
                          "Washington, D.C.",
                          "Xanthi",
                          "Yokohama",
                          "Zurich"]
    @State private var filteredContent: [String] = []

searchText will handle the text you type in the search bar, cities is an array that serves as the list’s datasource while filteredContent is the list’s second datasource that contains the result of data based on the user input to the search bar.

You can notice that both searchText and filteredContent are wrapped with the @State property wrapper, this is because we need to allow SwiftUI to automatically update the view when these values change. You will see how this is done next.

Replace the body variable implementation to the following:

var body: some View {
    NavigationStack {
        List(filteredContent, id: \.self) { city in
            VStack {
                Text(city)
            }
        }
    }
    .onAppear { filteredContent = cities }
    .searchable(text: $searchText)
    .onChange(of: searchText) {
        filteredContent = searchText.isEmpty ? cities : cities.filter { $0.lowercased().contains(searchText.lowercased()) }
    }
}

The List will use filteredContent array as a permanent datasource, however, when the view is appeared filteredContent will clone the content from the cities array. This allows to keep cities as a static source of truth that is needed on some use cases (i.e: on view appear and on search bar text reset).

Whenever a user types in the search bar, the onChange modifier callback is called, at that time the filteredContent is changed based on two condition:

1- If the search bar is empty, the cities array will be cloned in filteredContent array and populated in the List.
2- If there is any text typed, we will filter over the cities array, look for any element that contains the searched text string and add it to the filteredContent array, then populate the List with the result.

The paragraph can be revised for clarity and brevity as follows:

Notice the id: \.self in the List initializer. Since each element needs a unique identifier, in this case self is used as unique identifier for each item of the list. This would change if the data source were a custom object model instead of simple strings. The unique identifier helps SwiftUI differentiate between the items and track their state. Its counterpart in UIKit is the UITableView cell reuse identifiers.

References: https://developer.apple.com/documentation/swiftui/adding-a-search-interface-to-your-app

Thanks for reading – see you in the next digest!


Discover more from SweetTutos

Subscribe to get the latest posts sent to your email.

Malek
Software craftsman with extensive experience in iOS and web development.