Implementing a Search Bar in SwiftUI

In modern iOS app development, search functionality is a crucial component of user experience. SwiftUI, Apple’s declarative UI framework, offers straightforward methods for implementing a search bar. A well-implemented search bar can significantly improve content discovery within an application, enhancing usability and user satisfaction. This post will guide you through implementing a robust and efficient search bar in SwiftUI.

What is a Search Bar?

A search bar is a UI element that allows users to type in keywords to find specific content within an application. It typically consists of a text field for input and functionality to filter and display search results dynamically.

Why Implement a Search Bar?

  • Improved User Experience: Enables users to quickly locate content.
  • Enhanced Content Discovery: Makes it easier to find specific items within a large dataset.
  • Increased User Engagement: Keeps users engaged by providing relevant and immediate results.

How to Implement a Search Bar in SwiftUI

To implement a search bar in SwiftUI, you’ll primarily use the @State property wrapper, TextField, and the .searchable modifier.

Step 1: Create a Basic List View

First, create a basic list of items that the search bar will filter. Here’s an example:

import SwiftUI

struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry", "Date", "Fig", "Grape"]
    
    var body: some View {
        NavigationView {
            List(items, id: .self) { item in
                Text(item)
            }
            .navigationTitle("Fruit List")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Step 2: Add a Search Bar Using .searchable

Now, add the .searchable modifier to the List. This modifier is available in iOS 15 and later and simplifies the integration of a search bar. Here’s how to do it:

import SwiftUI

struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry", "Date", "Fig", "Grape"]
    @State private var searchText = ""
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items.filter { searchText.isEmpty ? true : $0.contains(searchText) }, id: .self) { item in
                    Text(item)
                }
            }
            .navigationTitle("Fruit List")
            .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search for a fruit")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

In this example:

  • @State private var searchText = "": Declares a state variable to hold the current search text.
  • .searchable(text: $searchText, prompt: "Search for a fruit"): Attaches a search bar to the List and binds it to the searchText state variable. The prompt provides a hint to the user.
  • placement: .navigationBarDrawer(displayMode: .always) makes sure the search bar always appears in the navigation bar

Step 3: Implement Filtering

Modify the List to filter the items based on the search text:

import SwiftUI

struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry", "Date", "Fig", "Grape"]
    @State private var searchText = ""
    
    var filteredItems: [String] {
        if searchText.isEmpty {
            return items
        } else {
            return items.filter { $0.localizedCaseInsensitiveContains(searchText) }
        }
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(filteredItems, id: .self) { item in
                    Text(item)
                }
            }
            .navigationTitle("Fruit List")
            .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search for a fruit")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Here’s what changed:

  • A computed property filteredItems is used to return a list of filtered items.
  • The filter function is used along with localizedCaseInsensitiveContains to perform a case-insensitive search.

Step 4: Enhance Search Suggestions (iOS 16+)

In iOS 16 and later, you can add search suggestions to guide users. Use the .searchSuggestions modifier:

import SwiftUI

struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry", "Date", "Fig", "Grape"]
    @State private var searchText = ""
    @State private var suggestedItems: [String] = []
    
    var filteredItems: [String] {
        if searchText.isEmpty {
            suggestedItems = []
            return items
        } else {
            suggestedItems = items.filter { $0.localizedCaseInsensitiveContains(searchText) }
            return suggestedItems
        }
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(filteredItems, id: .self) { item in
                    Text(item)
                }
            }
            .navigationTitle("Fruit List")
            .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search for a fruit") {
                ForEach(suggestedItems, id: .self) { suggestion in
                    Text(suggestion)
                        .searchCompletion(suggestion)
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Key improvements:

  • The .searchable modifier now includes a trailing closure that defines the search suggestions.
  • .searchCompletion(suggestion) populates the search bar with the selected suggestion when tapped.
  • suggestedItems property displays suggested list of itesm, based on text written on search bar.

Customizing the Search Bar

You can customize the appearance and behavior of the search bar further:

  • Placeholder Text: Use the prompt parameter in .searchable to change the placeholder text.
  • Scope Buttons: For more complex filtering, add scope buttons using the scope parameter (available in later iOS versions).
  • Custom Styles: You can use various view modifiers to adjust the appearance of the text field and list.

Conclusion

Implementing a search bar in SwiftUI is straightforward, especially with the .searchable modifier introduced in iOS 15. By leveraging this modifier, you can quickly add robust search functionality to your iOS applications, providing users with a seamless content discovery experience. Customizing the search bar with suggestions and advanced filtering options can further enhance usability and engagement, making your apps more intuitive and user-friendly.