Setting up the storyboard

Lets start with the storyboard & add in the components that we are going to need to make this work.

  1. First add a TableView & Search Bar onto you view controller. Arrange them to make it look decent.
  2. Time to hook these components up to the code. Open the assistant editor and while holding the control key on your keyboard drag them into the code. We are going to want to hook both of these components up as outlets.
    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var searchResultsTable: UITableView!

Setting up the View Controller

Now comes the fun part - actually coding this thing together! 🤓 Don't worry apple has made this surprisingly easy to do.

1. Set up the table view

The first thing we are going to want to do is set up the table view. If you have set up a table view before this should be very familiar. We are going to conform to two different protocols: UITableViewDataSource & UITableViewDelegate . UITableViewDataSources defines the data that we are going to display and UITableViewDelegate defines what happens when we click on a cell in the table view. For our simple example we are just going to print the name and the coordinates of the selected location.


// Setting up extensions for the table view
extension ViewController: UITableViewDataSource {
    // This method declares the number of sections that we want in our table.
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1

    // This method declares how many rows are the in the table
    // We want this to be the number of current search results that the
    // searchCompleter has generated for us
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return searchResults.count

    // This method delcares the cells that are table is going to show at a particular index
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // Get the specific searchResult at the particular index
        let searchResult = searchResults[indexPath.row]

        //Create  a new UITableViewCell object
        let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)

        //Set the content of the cell to our searchResult data
        cell.textLabel?.text = searchResult.title
        cell.detailTextLabel?.text = searchResult.subtitle

        return cell


extension ViewController: UITableViewDelegate {
    // This method declares the behavior of what is to happen when the row is selected
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        let result = searchResults[indexPath.row]
        let searchRequest = MKLocalSearch.Request(completion: result)

        let search = MKLocalSearch(request: searchRequest)
        search.start { (response, error) in
            guard let coordinate = response?.mapItems[0].placemark.coordinate else {

            guard let name = response?.mapItems[0].name else {

            let lat = coordinate.latitude
            let lon = coordinate.longitude



And finally we are going to want to tell our ViewController to use these for both the data source & delegate. This can be done in the viewDidLoad() method.

override func viewDidLoad() {
    searchResultsTable?.delegate = self
    searchResultsTable?.dataSource = self

Great our table view is ready to go! 🎉

2. Setting Up the Search Completer

The first thing that we are going to want to do is to import MapKit which is used by MKLocalSearchCompleter. You can do this by simply importing it in the top of the file.

import UIKit
import MapKit

Next, we are going to want to create an instance of the MKLocalSearchCompleter and also create an empty array of type MKLocalSearchCompletion which will act as our searchResults.

// Create a seach completer object
var searchCompleter = MKLocalSearchCompleter()

// These are the results that are returned from the searchCompleter & what we are displaying
// on the searchResultsTable
var searchResults = [MKLocalSearchCompletion]()

After that it is time to conform to more protocols, this time the MKLocalSearchCompleterDelegate & UISearchBarDelegate protocols.


// This method declares that whenever the text in the searchbar is change to also update
// the query that the searchCompleter will search based off of
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    searchCompleter.queryFragment = searchText


// This method declares gets called whenever the searchCompleter has new search results
// If you wanted to do any filter of the locations that are displayed on the the table view
// this would be the place to do it.
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
    // Setting our searcResults variable to the results that the searchCompleter returned
    searchResults = completer.results

    // Reload the tableview with our new searchResults

// This method is called when there was an error with the searchCompleter
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
    // Error

And then update the viewDidLoad method to set the delgates

override func viewDidLoad() {

    //Set up the delgates & the dataSources of both the searchbar & searchResultsTableView
    searchCompleter.delegate = self
    searchBar?.delegate = self
    searchResultsTable?.delegate = self
    searchResultsTable?.dataSource = self

That should be it! If you are having any problems please look at the GitHub repo which will have the full working code.

Swift 5 - Location Search with Auto Complete Location Suggestions

How to implement location search with auto complete location suggestions in Swift 5