DEV Community

Cover image for Swift 5 - Location Search with Auto Complete Location Suggestions📍🗺️
Jeff Edmondson
Jeff Edmondson

Posted on • Edited on • Originally published at jeffedmondson.dev

Swift 5 - Location Search with Auto Complete Location Suggestions📍🗺️

GitHub Repo
Alt Text

Setting up the storyboard

Let's 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!
Enter fullscreen mode Exit fullscreen mode

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.

UITableViewDataSource

// 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 declares 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
  }
}

Enter fullscreen mode Exit fullscreen mode

UITableViewDelegate

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 {
                return
            }

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

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

       print(lat)
       print(lon)
       print(name)

     }
  }
}
Enter fullscreen mode Exit fullscreen mode

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() {
    super.viewDidLoad()
    searchResultsTable?.delegate = self
    searchResultsTable?.dataSource = self
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 search 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]()
Enter fullscreen mode Exit fullscreen mode

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

UISearchBarDelegate

// 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
}
Enter fullscreen mode Exit fullscreen mode

MKLocalSearchCompleterDelegate

// 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 searchResults variable to the results that the searchCompleter returned
    searchResults = completer.results

    // Reload the tableview with our new searchResults
    searchResultsTable.reloadData()
}

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

And then update the viewDidLoad method to set the delegates

override func viewDidLoad() {
    super.viewDidLoad()

    //Set up the delegates & the dataSources of both the searchbar & searchResultsTableView
    searchCompleter.delegate = self
    searchBar?.delegate = self
    searchResultsTable?.delegate = self
    searchResultsTable?.dataSource = self
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)