Replicating the iOS 11 App Store search tab

Lately I’ve been working with UISearchController for a friends open source app and decided to share the approach we took to replicate the App Store search functionality.

The idea was to categorize the search type and use a state container view controller to swap the resulting view controller depending on the search type.

Here’s an overview of the hierarchy:

Having our components split this way and using UISearchController means no handling of the current search state. We won’t have if filtered {} spread arround a massive view controller that changes the entire structure if the user types something on the searchbar.

Using SearchResultsController

Taking advantage of the implementation of UISearchController searchResultsController makes it magically to switch between our original content and our filtered one without ever handing a search status. This approach becomes even easier when displaying the same UI as we’re currently showing as you can reuse the UIViewController but this is not our case.

searchController = UISearchController(
  searchResultsController: resultsContainerViewController
)
// Selecting a suggestion term
resultsContainerViewController.didSelect = search
// Search bar delegate to determine the type of search being performed
searchController.searchBar.delegate = self
searchController.searchBar.placeholder = "Search"
searchController.searchResultsUpdater = self
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
definesPresentationContext = true

Differentiating SearchType

enum SearchType {
  case partial
  case final
}

Our flow can go two ways, while typing the ‘suggestion terms’ screen will pop up showing a few app recommendations you might be looking for. When you’re done and tap search or tap on a trending term or suggestion the apps screen will be displayed and fetch the apps matching that term.

We use the search(term: String) method when tapped on a term. This will define the searchType and activate the UISearchController.

private func search(term: String) {
  searchController.searchBar.text = term
  searchType = .final
  searchController.isActive = true
}

Each time we type something we define that the searchType is .partial so our result controller will display the suggestions screen. Unless the text is empty.


extension SearchViewController: UISearchBarDelegate {
  func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    searchType = searchText.isEmpty ? .final : .partial
    navigationController?.navigationBar
	  .setShadow(hidden: searchText.isEmpty)
  }
}

Displaying results

Finally, since all our view controller swapping code is handled by the result view controller we only need to implement UISearchResultUpdating to perfom an action everytime the user types in the searchbar. updateSearchResults gets called everytime there is interaction with the searchController so it would be useful to handle those cases too.

extension SearchViewController: UISearchResultsUpdating {
  func updateSearchResults(for searchController: UISearchController) {
    guard let text = searchController.searchBar.text,
	 !text.isEmpty else {
	   return
	 }
	 resultsContainerViewController.handle(
	 term: text,
	 searchType: searchType
    )
  }
}

Our ResultsContainerViewController handle(term: , searchType:) implementation will look like this:

class ResultsContainerViewController: ContentStateViewController {
  func handle(term: String, searchType: SearchType) {
    switch searchType {
	 case .partial:
	   suggestionsViewController.searchedTerm = term
	   transition(to: .render(suggestionsViewController))
	case .final:
	  appsListViewController.reset()
	  appsListViewController.search(term: term)
	  transition(to: .render(appsListViewController))
	}
  }
}

The key to easily swap view controllers on ResultsContainerViewController class builds over the concept of child view controllers, you can read more about it these two articles by John Sundell: Custom container view controllers in Swift and Using child view controllers as plugins in Swift

This lets us switch between the suggestions and app results view controllers easily depending on the user’s action by adding/removing childs view controllers.

Final result:

All the code of this post can be found on a working example project on App Store Search