What’s New With UISearchController and UISearchBar

In this UISearchController tutorial, you’ll learn about UISearchToken, UISearchTextField and other new APIs introduced in iOS 13. By Corey Davis.

4.9 (11) · 1 Review

Download materials
Save for later
Share

UISearchBar and UISearchController are staples of iOS app development. But while UISearchBar has received periodic changes since its introduction in iOS 2, UISearchController has been pretty static since Apple introduced it in iOS 8. In iOS 13, Apple updated both.

Apple also introduced UISearchToken, which provides much-needed power to UISearchController. With very little effort, you can enable your users to perform complex search queries in addition to the text-based searches they’re used to.

If you’ve ever written tedious, fragile code to traverse the search bar’s view hierarchy to get a reference to the search text field, there is more good news. The UISearchTextField is now exposed as a property, making customization much easier.

In this tutorial, you’ll learn:

  • How to control the search results controller’s display.
  • Everything there is to know about search tokens.
  • How to create and use search tokens.
  • How to customize UISearchController, the search bar and its text field.
Note: If UISearchController is new to you, read UISearchController Tutorial: Getting Started first.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

Throughout this tutorial, you’ll work on L.I.S.T.E.D., the Large International Sorted Tally of Earth Dwellers. L.I.S.T.E.D. is an organization that keeps track of the total population of the world. They currently have populations for all countries for the years 2018 and 2019.

Anticipating the release of 2020 population data, they want to expand their search capabilities. As an Earth dweller yourself, you’re going to help refactor the app and bring its search functionality to a whole new level for the organization.

Start by opening LISTED.xcodeproj inside the starter folder, then open Main.storyboard.

The storyboard for the app

You’ll see the app is pretty simple. There are only two view controllers, which are wrapped inside a navigation controller.

Build and run. You’ll see this:

The app prior to any modifications. It displays a table view with a list of countries and population data

Now, search for “new”. You’ll see these search results:

Search results for countries that contain the word 'new' showing population data for 2018 and 2019.

The result displays matches for all years. Tapping 2018 or 2019 in the scope bar will narrow the search. Try it now by tapping 2019 and this is what you’ll see:

Search results for countries that contain the word 'new' showing only 2019 population data.

Great, basic searching works fine. Now, you’ll make it better!

Using the Search Results Controller

UISearchController provides two options for displaying results: displaying them in the same view that shows the original data or using a search results controller.

L.I.S.T.E.D. uses a search results controller to display the results in a slightly different format from the main controller. When the user taps into the search bar, the main view controller remains visible. The result view controller displays after the user starts typing the search.

Before iOS 13, you had little control over this behavior, but now you can use the newly-added showsSearchResultsController in UISearchController to customize your results. You’ll see how in the next sections.

Displaying Results: You’re in Control

To control when search results display, you need to react to changes in the search bar. For this to work, the main view controller will conform to UISearchResultsUpdating.

The delegate receives a call to updateSearchResults(for:) when the search bar becomes the first responder or when text changes. You’ll use this to trigger the display of the results controller.

Open MainViewController.swift and add the following after UISearchBarDelegate:

// MARK: -

extension MainViewController: UISearchResultsUpdating {
  func updateSearchResults(for searchController: UISearchController) {
    searchController.showsSearchResultsController = true
  }
}

The search results now display when the search bar becomes the first responder.

Before you can test that, add the following code as the last line in viewDidLoad():

searchController.searchResultsUpdater = self

The search results updater is responsible for updating the search results controller. Here, you’re assigning that responsibility to the main controller.

Build and run. Tap the search bar and you’ll see an empty search results controller.

An empty search results view.

You’re now in full control of showing — and hiding — the search results. As powerful as you must feel now, showing a blank results controller is not a great user experience. You’ll address that soon, but first, you need to know more about search tokens.

Everything You Need to Know About Search Tokens

Search tokens are arguably the most interesting feature Apple added to search in iOS 13. If you use Apple’s Mail or Photos apps on iOS 13, you’ve likely already seen search tokens in action.

The Mail app uses search tokens to create complex searches. Tapping the search bar shows suggestions like “unread messages” and “flagged messages”.

Apple's Mail app displaying the search results view. The view displays a list of suggested searches.

Tokens can represent complex searches like searching by geolocation or simple searches using predetermined text. The key to search tokens is representedObject in UISearchToken.

representedObject is an Any? and can contain any type of data that’s useful to you.

It’s important to keep in mind that representedObject is strongly referenced. Any object it holds may stick around for quite some time. Use lightweight data to avoid problems. Strings, Ints and NSManagedObjectIDs are great candidates.

Creating Tokens

It’s time to address the empty search results view you see when you tap the search bar. This is a great place to show a list of tokens available to your users.

Open ResultsTableViewController.swift. At the top of the class, after countries, add the following:

var searchTokens: [UISearchToken] = []

Here, you’re creating an array to hold the search tokens. After that line, add the following:

var isFilteringByCountry: Bool {
  return countries != nil
}

This computed Boolean will return true when users are searching or false when they’re not. You’ll use it soon.

At the end of the file, below the class, add the following extension:

// MARK: -

extension ResultsTableViewController {
  func makeTokens() {
    // 1
    let continents = Continent.allCases
    searchTokens = continents.map { (continent) -> UISearchToken in
      // 2
      let globeImage = UIImage(systemName: "globe")
      let token = UISearchToken(icon: globeImage, text: continent.description)
      // 3
      token.representedObject = Continent(rawValue: continent.description)
      // 4
      return token
    }
  }
}

This code does the following:

  1. Creates an array of all continents.
  2. Creates an image that represents the token. Next, using the image and current continent’s description, it creates a search token.
  3. Assigns the continent’s description to the token’s representedObject. You’ll use this later to narrow searches to specific continents. Using a lightweight value such as a string is perfect for this situation.
  4. Returns the token, which appends to searchTokens, which you created earlier.

In viewDidLoad(), add this as the last line:

makeTokens()

Here, you create the search tokens when the view loads. That’s it! Creating search tokens is that simple. :]

Happy iPhone with search icon

Before you build and run again, you need to update the results controller to display these new tokens. You’ll do that in the next step.