Swift and SwiftUI tutorials for Swift Developers

searchFocused in SwiftUI

As an iOS Developer, you know that User Experience (UX) is everything. One of the most common interactions in any modern application is search. Users expect to find what they need quickly, and any friction in this process can result in frustration. This is where Swift programming shines, and more specifically, Apple’s declarative framework.

In this article, we will take a deep dive into what the searchFocused modifier in SwiftUI is, how it interacts with the view’s state, and how you can implement it using Xcode to create fluid and intuitive search experiences across the entire Apple ecosystem: iOS, macOS, and watchOS.


What is searchFocused in SwiftUI?

Introduced in recent SwiftUI updates, searchFocused is a view modifier (an “instance method” in Apple’s documentation jargon) that allows you to programmatically control the focus state of a search field generated by the .searchable modifier.

Before the arrival of focus APIs in SwiftUI, controlling when the keyboard appeared or disappeared in a search bar required “hacks” using UIViewRepresentable to access the underlying UIKit API (UISearchBar). Today, using Swift, we can achieve this in a purely declarative way.

The Anatomy of Focus

To use searchFocused, you need to understand its inseparable companion: @FocusState.

A @FocusState is a property wrapper that SwiftUI uses to track which user interface element currently has the system focus (for example, which one is receiving keyboard input). By binding a @FocusState to the searchFocused modifier, you can:

  1. Read if the search bar is currently focused.
  2. Force the search bar to take focus (automatically bringing up the keyboard).
  3. Remove focus from the search bar (dismissing the keyboard).

Prerequisites and Development Environment

To get the most out of this tutorial and write the code in Xcode, make sure you meet the following requirements:

  • Environment: Xcode 13 or higher (Xcode 15+ is recommended for the latest optimizations).
  • Language: Swift 5.5 or higher.
  • Operating Systems:
    • iOS 15.0+
    • macOS 12.0+
    • watchOS 8.0+

Step-by-Step Implementation on iOS

Let’s start with the most common platform for an iOS Developer. We are going to create a list of items and add a search bar to it. We want the cursor to automatically jump to the search bar when a specific button is pressed.

1. Preparing the Base View

First, we set up our view with a list of data and the .searchable modifier.

import SwiftUI

struct iOSSearchView: View {
    @State private var searchText = ""
    let items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]
    
    var filteredItems: [String] {
        if searchText.isEmpty {
            return items
        } else {
            return items.filter { $0.localizedCaseInsensitiveContains(searchText) }
        }
    }
    
    var body: some View {
        NavigationStack {
            List(filteredItems, id: \.self) { item in
                Text(item)
            }
            .navigationTitle("Fruits")
            // Here we add the search capability
            .searchable(text: $searchText, prompt: "Search fruits...")
        }
    }
}

2. Integrating @FocusState and searchFocused

Now, let’s add the focus control. We will declare a @FocusState variable and bind it using searchFocused in SwiftUI.

import SwiftUI

struct iOSSearchView: View {
    @State private var searchText = ""
    
    // 1. Declare the focus state
    @FocusState private var isSearchFieldFocused: Bool
    
    let items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]
    
    var filteredItems: [String] {
        if searchText.isEmpty { return items }
        return items.filter { $0.localizedCaseInsensitiveContains(searchText) }
    }
    
    var body: some View {
        NavigationStack {
            VStack {
                // Button to force focus
                Button("Start searching!") {
                    // 3. Modify the state to activate the keyboard
                    isSearchFieldFocused = true
                }
                .padding()
                .buttonStyle(.borderedProminent)
                
                List(filteredItems, id: \.self) { item in
                    Text(item)
                }
            }
            .navigationTitle("Fruits")
            .searchable(text: $searchText, prompt: "Search fruits...")
            // 2. Bind the focus state to the search field
            .searchFocused($isSearchFieldFocused)
        }
    }
}

Code Analysis:

  • By declaring @FocusState private var isSearchFieldFocused: Bool, we tell SwiftUI to track a boolean value.
  • The .searchFocused($isSearchFieldFocused) modifier establishes a two-way binding. If the user taps the search bar, the variable becomes true. If we press our button, we change the variable to true via code, and SwiftUI reacts by giving focus to the bar and showing the keyboard.

Adapting the Code for macOS

Modern Swift programming allows us to reuse much of our code, but each platform has its nuances. On macOS, we don’t have an on-screen keyboard that pops up, but the concept of “focus” is still crucial so the user can start typing immediately without having to click with the mouse.

In Xcode, you can reuse almost exactly the same code for macOS, but the location of the search field often resides in the toolbar.

import SwiftUI

struct MacOSSearchView: View {
    @State private var searchText = ""
    @FocusState private var isSearchFocused: Bool
    
    let items = ["Xcode", "Swift", "SwiftUI", "Instruments", "Core Data"]
    
    var filteredItems: [String] {
        if searchText.isEmpty { return items }
        return items.filter { $0.localizedCaseInsensitiveContains(searchText) }
    }
    
    var body: some View {
        NavigationStack {
            List(filteredItems, id: \.self) { item in
                Text(item)
            }
            .navigationTitle("Mac Tools")
            .searchable(text: $searchText, placement: .toolbar, prompt: "Search tool...")
            .searchFocused($isSearchFocused)
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    Button(action: {
                        // Gives focus to the search field in the toolbar
                        isSearchFocused = true
                    }) {
                        Image(systemName: "magnifyingglass")
                    }
                    .help("Go to search") // Native macOS tooltip
                }
            }
        }
    }
}

Note for the iOS Developer: Notice how on macOS we use placement: .toolbar within .searchable to fit Apple’s desktop Human Interface Guidelines (HIG).


Taking it to the Wrist: watchOS

Implementing searchFocused in SwiftUI for watchOS presents a unique challenge due to the screen size. On watchOS, search often brings up a dictation screen or a full-screen “Scribble/QWERTY” keyboard.

Fortunately, the modifier behaves intelligently and natively.

import SwiftUI

struct WatchOSSearchView: View {
    @State private var searchText = ""
    @FocusState private var isSearchFocused: Bool
    
    let contacts = ["Tim", "Craig", "Joz", "Phil"]
    
    var filtered: [String] {
        searchText.isEmpty ? contacts : contacts.filter { $0.contains(searchText) }
    }
    
    var body: some View {
        NavigationStack {
            List {
                Button("Search Contact") {
                    // Immediately triggers the watchOS text input interface
                    isSearchFocused = true
                }
                .foregroundColor(.blue)
                
                ForEach(filtered, id: \.self) { contact in
                    Text(contact)
                }
            }
            .navigationTitle("Contacts")
            .searchable(text: $searchText)
            .searchFocused($isSearchFocused)
        }
    }
}

Advanced Use Cases in SwiftUI

Multiple Focus Fields with Enums

Sometimes we don’t just track a boolean value, but we have multiple text fields in a view (for example, a form and a search bar). For this, a good iOS Developer uses an enum.

enum FocusField: Hashable {
    case searchBar
    case usernameField
    case passwordField
}

struct ComplexFormView: View {
    @State private var searchText = ""
    @State private var username = ""
    @FocusState private var focusedField: FocusField?
    
    var body: some View {
        NavigationStack {
            Form {
                TextField("Username", text: $username)
                    .focused($focusedField, equals: .usernameField)
                
                Button("Go to search") {
                    focusedField = .searchBar
                }
            }
            .searchable(text: $searchText)
            // We use the method variant that accepts a comparison value
            .searchFocused($focusedField, equals: .searchBar)
        }
    }
}

This is the beauty of Swift programming: Type Safety. By using an Enum, we eliminate the risk of having multiple booleans in contradictory states.

Auto-focus on Appear

A common UX pattern is to open a search view and have the keyboard already visible.

To achieve this, we might think of using .onAppear, but due to how SwiftUI renders views, we often need a slight delay to allow the view to build the .searchable field before trying to focus it. We can use .task for this:

.task {
    // Gives a fraction of a second for the UI to settle
    try? await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds
    isSearchFieldFocused = true
}

Summary of Behaviors by Platform

For a quick reference in your next Xcode project, here is how the behavior of searchFocused differs by platform:

Platform Behavior when searchFocused = true is activated Primary input method
iOS The search field receives a focus ring (or cursor) and the virtual keyboard slides up. On-screen or physical keyboard.
macOS The search field (usually in the Toolbar) gets a blue focus ring (or your accent color). Physical keyboard.
watchOS Deploys the native full-screen input interface (Scribble or Dictation). Voice (Dictation), Scribble, or Keyboard.

Conclusion

Using searchFocused in SwiftUI represents a massive leap forward in how developers interact with the user interface declaratively. It has eliminated the need to deal with complex delegates and UIKit or AppKit controllers, keeping Swift programming clean, concise, and easy to maintain.

As an iOS Developer, mastering focus management not only makes your Xcode code more elegant, but it directly impacts the final quality of your app. A user who doesn’t have to stretch their thumb to tap a tiny search bar—because you anticipated it and focused it programmatically—is a happy user.

If you have any questions about this article, please contact me and I will be happy to help you 🙂. You can contact me on my X profile or on my Instagram profile.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

How to add Close Button to a Sheet in SwiftUI

Next Article

ListStyle in SwiftUI

Related Posts