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.
Get up to $2500 signing bonus applying as Software Developer
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 GitHub or
Related Posts
Solving ambiguous constraints without rerunning your app
Dealing with Auto Layout Solving Auto Layout issues is always a hassle; we run our application expecting all our constraints work correctly to find a massive block of Auto Layout... Keep reading