Add Search to Table View iOS Tutorial

Most modern iOS apps have a feature of a search bar to search for items in a Table View. This tutorial will show how to add the search functionality to a Table View which let the user search through the items by specifying a search term. This tutorial is made with Xcode 10 and built for iOS 12.

Open Xcode and create a new Single View App.

single-view-xcode-template.png

For product name, use IOSAddSearchTableViewTutorial and then fill out the Organization Name and Organization Identifier with your customary values. Enter Swift as Language and choose Next.

Remove the View Controller from the Storyboard and drag a Navigation Controller to the empty canvas. When the initial View Controller is deleted there isn't a starting point defined. Select the Navigation Controller and go to the Attribute Inspector. In the View Controller Section  select the "Is Initial View Controller" checkbox.

Double-click on the Navigation Bar in The Table View Controller and set the title to "Numbers".  Select the Table View Cell and go to the Attributes Inspector. In the Table View Cell section set the Identifier  to "Cell".

The Storyboard should look like this.

Since we have deleted the View Controller from the Storyboard we can also delete the ViewController.swift file. Add a new file to the project, select iOS->Source->Cocoa Touch Class. Name it TableViewController and make it a subclass of UITableViewController.

The TableViewController class needs to be linked to The Table View Controller object in the Storyboard. Select it and go the Identity Inspector. In the Custom Class section change the class to TableViewController.

Go to the TableViewController.swift file and change the class declaration line

class TableViewController: UITableViewController, UISearchResultsUpdating {

The UISearchResultsUpdating protocol updates the search results, later we will add a delegate method to let the TableViewController conform to this protocol. Add the following properties

let tableData = ["One","Two","Three","Twenty-One"]
var filteredTableData = [String]()
var resultSearchController = UISearchController()

The tableData property will hold the items of the Table View, where the filteredTableData property will contain the result from the search query. The Search Controller manages the results of the search. Change the viewDidLoad method to 

 override func viewDidLoad() {
    super.viewDidLoad()

    resultSearchController = ({
        let controller = UISearchController(searchResultsController: nil)
        controller.searchResultsUpdater = self
        controller.dimsBackgroundDuringPresentation = false
        controller.searchBar.sizeToFit()

        tableView.tableHeaderView = controller.searchBar

        return controller
    })()

    // Reload the table
    tableView.reloadData()
}

A closure is used which is assigned to the resultSearchController. The results of the search will be presented in the current Table View, so the searchResultsController parameter of the UISearchController init method is set to nil. Also, the searchResultsUpdater property is set to the current Table View Controller and the background dimming is set to false. The searchable is added to the Table View and the data is reloaded. Next, implement the Table View Data Source delegate methods

 override func numberOfSections(in tableView: UITableView) -> Int {
   // 1 
   // return the number of sections
   return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  // 2
  // return the number of rows
  if  (resultSearchController.isActive) {
      return filteredTableData.count
  } else {
      return tableData.count
  }
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  // 3
  let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

  if (resultSearchController.isActive) {
      cell.textLabel?.text = filteredTableData[indexPath.row]

      return cell
  }
  else {
      cell.textLabel?.text = tableData[indexPath.row]
      print(tableData[indexPath.row])
      return cell
  }
}
  1. Only One section is being used

  2. If the Search Controller is active the number of filtered items are assigned to the number of rows in the Table View, otherwise the tableData array is being used.

  3. If the Search Controller is active, the filtered items are displayed, otherwise the original items of the tableData array are displayed.

Finally, implement the updateSearchResults(for:_) delegate method of the UISearchResultsUpdating protocol

func updateSearchResults(for searchController: UISearchController) {
    filteredTableData.removeAll(keepingCapacity: false)

    let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %@", searchController.searchBar.text!)
    let array = (tableData as NSArray).filtered(using: searchPredicate)
    filteredTableData = array as! [String]

    self.tableView.reloadData()
}

The updateSearchResults(for:_) method is called when the user updates the Search bar with input. In this method we will handle the search filtering of our search term. NSPredicate is an sort of expression that handles the search criteria. "c" means case-sensitive. The results are then assigned to the filteredTableData array and the Table View is reloaded. Build and Run the project, enter a search query and the results are being displayed.

You can download the source code of the IOSAddSearchTableViewTutorial at the ioscreator repository on Github.