Sample CodeiOS, iPadOS, Mac CatalystReviewed 2026-07-21View on Apple Developer

Using suggested searches with a search controller

At a glance

Item Summary
Purpose Present suggested search tokens, filter products, navigate results, and restore search state with UISearchController.
App architecture MainTableViewController owns the product list, UISearchController, and results controller; ResultsTableController switches between suggestions and filtered products, then reports selections through the local weak SuggestedSearch delegate.
Main patterns Controller-owned search adapter, Weak local delegate, Token-aware filtering
Project style Eight Swift files; a primary table controller with focused extensions for search updates and suggested-search callbacks.

Project structure

Source bundle/
├── SuggestedSearch/
│   ├── ResultsTableController.swift
│   ├── MainTableViewController.swift
│   ├── AppDelegate.swift
│   ├── DetailViewController.swift
│   ├── SceneDelegate.swift
│   ├── Product.swift
│   ├── MainTableViewController+ResultsUpdate.swift
│   ├── MainTableViewController+SuggestedSearch.swift
│   └── Base.lproj/
│       └── LaunchScreen.storyboard
├── Configuration/
│   └── SampleCode.xcconfig
└── SuggestedSearch.xcodeproj/
    ├── .xcodesamplecode.plist
    └── project.pbxproj

Structure observations

  • Search construction, presentation, and restoration belong to the primary controller rather than app lifecycle delegates.
  • Results are a child adapter with their own table state, but product/filter authority remains with the primary controller.
  • The local delegate carries two user intents—insert a token or open a product—without retaining the main controller.

Overall architecture

Reference code

SuggestedSearch/MainTableViewController.swift:101 — search-controller composition

        resultsTableController = ResultsTableController()
        resultsTableController.suggestedSearchDelegate = self

        searchController = UISearchController(
            searchResultsController: resultsTableController)
        searchController.searchResultsUpdater = self
        searchController.searchBar.delegate = self
        searchController.delegate = self
        navigationItem.searchController = searchController
        definesPresentationContext = true

Interpretation

The main controller is the search feature root. UIKit sends text/token changes through UISearchResultsUpdating; the controller filters its canonical products and assigns the projection to the results controller. Selection travels back as intent through a narrow local protocol.

Ownership and state

Ownership evidence

SuggestedSearch/ResultsTableController.swift:19 — results projection and weak callback

class ResultsTableController: UITableViewController {
    var filteredProducts = [Product]()

    weak var suggestedSearchDelegate: SuggestedSearch?

    static let suggestedSearches = [
        NSLocalizedString("Red Flowers", comment: ""),
        NSLocalizedString("Green Flowers", comment: ""),
        NSLocalizedString("Blue Flowers", comment: "")
    ]
}
Owner Object or state Relationship Mutation authority
MainTableViewController Canonical products, search controller, results controller Creates and strongly stores all three feature collaborators It updates filtering, tokens, presentation, navigation, and restoration state.
UISearchController Search bar and results presentation Owned by the main controller/navigation item UIKit drives search/delegate callbacks.
ResultsTableController Filtered-product projection and suggestion-mode flag Stores result values and static suggestion labels It chooses row representation and emits selection intent.
ResultsTableController suggestedSearchDelegate Weak, class-bound reference The main controller owns the child; the callback must not create a cycle.
Product Title, date, price, color Value stored in product arrays Formatting helpers derive display values without owning UI.

Composition denotes source-visible construction and retained value state. Aggregation denotes a stored or injected collaborator whose exclusive lifetime the source does not prove.

Class and protocol design

SuggestedSearch/ResultsTableController.swift:10 — local results-to-owner contract

protocol SuggestedSearch: AnyObject {
    func didSelectSuggestedSearch(token: UISearchToken)
    func didSelectProduct(product: Product)
}

class ResultsTableController: UITableViewController {
    weak var suggestedSearchDelegate: SuggestedSearch?
}
Type Responsibility Depends on or conforms to
MainTableViewController Owns search composition, filtering source, restoration, and navigation UITableViewController, UISearchResultsUpdating, SuggestedSearch, search delegates
ResultsTableController Displays suggestions or filtered products and emits selection UITableViewController
SuggestedSearch Reports token/product selection intent Local class-bound protocol
DetailViewController Presents one product and restores detail state UIViewController
Product Searchable/serializable product data and formatting Codable value type

SuggestedSearch is the sample’s protocol-oriented seam: the results controller depends on two capabilities, not on MainTableViewController. UIKit search protocols handle framework events separately.

Access control

Symbol Access Verified effect Design reason
MainTableViewController.RestorationKeys private nested enum Usable only by the main controller’s lexical implementation Keeps scene-payload keys coupled to the state they encode.
setupDataModel private Only the main controller initializes the bundled product list Demo-data construction is not a child-controller command.
ResultsTableController.suggestedImage private Only result-cell rendering can create suggestion artwork Prevents callers from depending on a presentation helper.
Controllers, Product, and local protocol implicit internal Visible across the application target The app needs cross-file conformances but exports no library API.

Reference code

SuggestedSearch/MainTableViewController.swift:17 — controller-private restoration schema

    private enum RestorationKeys: String {
        case searchControllerIsActive
        case searchBarText
        case searchBarIsFirstResponder
        case searchBarToken
    }

    var searchController: UISearchController!
    var resultsTableController: ResultsTableController!
    var products = [Product]()

Private nested keys protect the controller’s restoration encoding, while the three collaborator properties remain internal so same-module extensions can use them. No public, open, or fileprivate feature API is present.

Logic ownership and placement

Logic Owning type or file Why it lives there
Search object construction and scene restoration MainTableViewController It owns the search UI and lifecycle state.
Text/token filtering MainTableViewController+ResultsUpdate Filtering reads canonical products and writes the results projection.
Suggestion/result row presentation ResultsTableController Only this controller knows which mode its table currently displays.
Token insertion and result navigation MainTableViewController+SuggestedSearch These actions mutate main navigation/search state after a child intent.
Product display formatting Product Price/year derivation is domain presentation data shared by tables/details.

Design patterns

Pattern Source evidence Purpose or tradeoff
Controller-owned search adapter SuggestedSearch/MainTableViewController.swift:101 Composes UIKit search objects around one canonical product owner.
Weak local delegate SuggestedSearch/ResultsTableController.swift:50 Returns semantic user intent without a parent-controller dependency or retain cycle.
Token-aware filtering SuggestedSearch/MainTableViewController+ResultsUpdate.swift:13 Combines free text and represented token state into one results projection.

Naming conventions

  • Controller roles are explicit (MainTableViewController, ResultsTableController, DetailViewController).
  • The local protocol names the interaction capability (SuggestedSearch); methods name emitted intents (didSelect...).
  • Mode and projection names are descriptive (showSuggestedSearches, filteredProducts).
  • Focused extensions are named for the conformance responsibility (ResultsUpdate, SuggestedSearch).

Architecture takeaways

  • Let the primary controller own canonical products and search composition; keep the child as a results projection.
  • Return token/product selection through a weak capability protocol rather than a concrete parent reference.
  • Keep restoration keys private and collaborators internal when same-module extensions need them.

Source map

Source file Architectural role
SuggestedSearch/MainTableViewController.swift Search composition, product source, presentation, and restoration
SuggestedSearch/MainTableViewController+ResultsUpdate.swift Text/token filtering
SuggestedSearch/MainTableViewController+SuggestedSearch.swift Local delegate conformance, token insertion, and navigation
SuggestedSearch/ResultsTableController.swift Suggestion/result projection and weak callback
SuggestedSearch/Product.swift Product value and display formatting
SuggestedSearch/DetailViewController.swift Selected-product presentation and restoration