Swift 5 - Location Search with Auto Complete Location Suggestions📍🗺️
Jeff Edmondson
Posted on June 25, 2020
Setting up the storyboard
Let's start with the storyboard & add in the components that we are going to need to make this work.
- First add a
TableView
&Search Bar
onto you view controller. Arrange them to make it look decent. - 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.
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
}
}
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)
}
}
}
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
}
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 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]()
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
}
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
}
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
}
That should be it! If you are having any problems please look at the GitHub repo which will have the full working code.
Posted on June 25, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.