TechnologiesSwiftUISearch and Find

SearchScopeActivation struct

iOSmacOStvOSwatchOSvisionOSiOS 16.4+✓ renders

The ways that searchable modifiers can show or hide search scopes.

How it works

SearchScopeActivation is a value type that tells SwiftUI when the scope bar attached to a searchable view should appear. Search scopes let people narrow a query to a subset of results, but showing those scope buttons at the wrong moment clutters the interface or hides them when they're needed. By passing a SearchScopeActivation constant to the searchScopes(_:activation:_:) modifier, you control whether the scopes reveal themselves as soon as search begins, only once the person starts typing, or whenever the search field is interacted with. Reach for it when the default activation timing doesn't match how your search field is meant to feel.

  1. Supply an activation to searchScopes(_:activation:_:)

    The activation parameter of the searchScopes modifier takes a SearchScopeActivation value that decides the moment the scope bar becomes visible. In the example, .searchScopes($scope, activation: .onTextEntry) binds the current scope selection and requests that the scopes surface as the person types.

  2. Bind the selected scope to state

    The first argument is a binding that records which scope is active so SwiftUI can update it as the selection changes. Here it is $scope, backed by the @State private var scope = Scope.all property, so the chosen scope drives the rest of the view.

  3. Choose the activation constant

    SearchScopeActivation exposes constants such as onTextEntry, which defers the scope bar until characters are entered, and onSearchPresentation, which shows it the moment search is presented. The example uses .onTextEntry so the All, Mail, and Files buttons stay out of the way until the query begins.

  4. Provide the scope buttons in the trailing closure

    The closure builds the content of the scope bar that SearchScopeActivation governs the appearance of. The ForEach(Scope.allCases, id: \.self) loop emits a Text(s.rawValue).tag(s) per case, and each tag value must match the type of the bound selection so a tap updates $scope.

  5. Anchor it to a searchable field

    The scopes and their activation only take effect alongside an active search field, so searchScopes is layered on the same view as .searchable(text: $query). SearchScopeActivation then times when that field's scope bar is revealed relative to the $query text being edited.

Try it — Change activation: .onTextEntry to activation: .onSearchPresentation and notice the All/Mail/Files scope bar now appears the instant you tap the search field, before typing a single character.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

SearchScopeActivation.swift
struct SearchScopeActivationDemo: View {
    @State private var query = ""
    @State private var scope = Scope.all

    enum Scope: String, CaseIterable {
        case all = "All"
        case mail = "Mail"
        case files = "Files"
    }

    var body: some View {
        NavigationStack {
            List {
                Text("Scope: \(scope.rawValue)")
                Text("Query: \(query)")
            }
            .navigationTitle("Search")
            .searchable(text: $query)
            .searchScopes($scope, activation: .onTextEntry) {
                ForEach(Scope.allCases, id: \.self) { s in
                    Text(s.rawValue).tag(s)
                }
            }
        }
        .padding()
    }
}
Live preview
Search Scope: {scope.rawValue} Query: 9:41 Search
Search Scope: {scope.rawValue} Query: 9:41 Search
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →