Google’s Place AutoComplete and Maps integration in iOS, Swift 4.


“person using Google map” by NESA by Makers on Unsplash

Google maps have now become quite common in all the apps that require maps functionality. There are many tutorials that beautifully describe how to integrate google maps in iOS apps. But, they all lack in describing the integration with PlaceAutocomplete SDK integration in iOS.

This tutorial covers everything you need to know to integrate custom Place Autocomplete Feature in iOS apps🤘.

Installation

Before we start coding, we must install the Google Maps and places iOS SDK first. You can use any dependency manager, but i would recommend using CocoaPods.

Create a Podfile inside your project root directory (using pod init, nano Podfile), and copy the following code:

# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'
target 'placeAutocomplete' do
use_frameworks!
# Pods for placeAutocomplete
pod 'GoogleMaps'
pod 'GooglePlacePicker'
pod 'GooglePlaces'
pod 'Alamofire', '~> 4.7'
pod 'SwiftyJSON', '~> 4.0'

All you need to do is to install the pods by giving command pod install in your project root directory through terminal.

GoogleMaps, GooglePlacePicker, GooglePlaces libraries are required for the maps SDK integration. Alamofire is a third party library that is used for network calls in iOS apps. SwiftyJSON is another third party library that is useful in easy JSON parsing.

Get the API key

To use the Google Maps iOS SDK, you will need an API key. To generate key you have to visit Google API Console.

Create a project, and navigate to ‘Credentials’ and Pick API key. You will need to provide your project Bundle ID. The key is generated by the unique bundle id, so if it’s changed, the Google Maps services won’t work🙈!

To enable autocomplete feature in your app, 2 more API’s have to be enabled viz. Places SDK for iOS and Geocoding API.

This can be done by clicking on Enable APIs and services button on the dashboard and enabling the above 2 APIs. At last your console should show the below 3 APIs in the list of enabled APIs.

Enabled APIs in console.

Go to your project, and in your AppDelegate.swift class add import GoogleMaps, import GooglePlaces. Then copy the following code to

_ application: UIApplication, didFinishLaunchingWithOptions

GMSServices.provideAPIKey("YOUR_API_KEY")        GMSPlacesClient.provideAPIKey("YOUR_API_KEY")

Your AppDelegate should look something like this.


Step1 — Set up google map

There are plenty of tutorials out there that show how to integrate Google Maps in your ViewController so i will not be dwelling too deep into it. But i will be showing the basic implementation necessary for autocomplete to work. The code that you will see here is tested in parallel as i write.

Lets begin⚡️

Go to your ViewController (storyboards 😍)where you have to add the map. Create a custom UIView with the size you need. Assign it the class of GMSMapView in the class tab.

Also add a button as a search bar that takes our app to the address search page. You can search on the same page but i used another page for simplicity purposes.

Finally we code!

Let’s get back to the ViewController and write some code ⌨. The below snippet shows the whole class so you can get a better picture of what is going on.

We confirm our GoogleMapViewController.swift class to CLLocationManagerDelegate so that can we are able to extract current location from the phone.

//implementing extension from CLLocationManagerDelegate
extension GoogleMapViewController: CLLocationManagerDelegate {

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

guard status == .authorizedWhenInUse else {
return
}

locationManager.startUpdatingLocation()

mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true

}

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

guard let location = locations.first else {
return
}

mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)

locationManager.stopUpdatingLocation()

}

}

Note: Don’t forget to add Privacy — Location When In Use Usage Description in the plist file or the app will crash while running.

plist file for the app.

Step2: Establishing segue to go to search page.

On clicking the search button our app should go to the search page where we will be implementing our AutoCompleteFeature.

I have named the view controller as autoCompleteSearchViewContoller.swift

Segue established between the two view controllers

Step 3: Implementing the AutoCompleteFeature

The autocomplete feature of google SDK provides functionality to get a list of places searched on the basis of few keywords. This is similar to that in the google maps.

Google provides a complete autocomplete UI implementation directly in the SDK but if you have to tinker and do changes then its a problem. I encountered this problem during my internship when i had to provide search results in another format than that provided by Google.

autocomplete UI provided by google

Now if you have to add other sections in the same tableview or show the results in another form, you can’t do changes in the google SDK (except from some themes).

Our implementation of search results being displayed.

To solve this we have to implement separate function provided in the API that will help us to fetch the place details from API, store it in array and display in tableView.

The following function helps us achieve this functionality.

GMSAutoCompleteFilter: This class represents a set of restrictions that may be applied to autocomplete requests. This allows customization of autocomplete suggestions to only those places that are of interest. There are different filter types viz.

  1. geocode
  2. Address
  3. establishment
  4. (regions)
  5. (cities)

Detailed description have been provided in the docs (above link). The above function takes string as input and gives details accordingly.

Step 4 : Using geocoding to extract locations

Geocoding is the process of converting addresses (like a street address) into geographic coordinates (like latitude and longitude), which you can use to place markers on a map, or position the map. Reverse geocoding is the process of converting geographic coordinates into a human-readable address.

We will use geocoding API to extract the exact details and coordinates from full place address that we achieved from placeAutoComplete function and stored in resultsArray.

The API url is:

"https://maps.googleapis.com/maps/api/geocode/json?address=\(correctedAddress)&sensor=false&key=\(self.googleAPIKey)"

We will be using alamofire and swifty json to extract information we want. I have implemented this functionality in didSelectRow method of tableView.

geocoding API provides a plethora of details about a place. Below snippet shows json for search of marathalli (a region in bangalore).

Right now we need just the latitude and longitude along with the complete address of the place. So we according extract that. I have used SwiftyJSON to achieve this.

let json = JSON(value)
let lat = json["results"][0]["geometry"]["location"]["lat"].rawString()      

let lng = json["results"][0]["geometry"]["location"]["lng"].rawString()

let formattedAddress = json["results"][0]["formatted_address"].rawString()

Here value is the json response we receive on our address.

Step 5: Time to combine placeAutocomplete and geocoding API 😎

The below snippet shows my integration of both into autocompleteSearchViewController.swift.

i have implemented searchController in the tableView programmatically and made different functions for proper working of the search controller. I prefer rayWenderlich’s tutorial for search controller a good place for learning the implementation. I have used the same methods as given in the tutorial so if you don’t understand the code you can refer to the tutorial.

Step5: Displaying the searched place on map

Now we have extracted the longitude as well as the latitude of the place 🤩. The only thing remaining now is to pin point location on the map.

This can be achieved in 2 ways:

  1. By using swift delegates and protocols.
  2. By using global variables.

For keeping the tutorial simple i have used global variables here but the recommended way is the one with the delegates.

Using global variables:

We need three global variables to store data viz. latitudes, longitudes and address.

var g_lat: String!
var g_long: String!
var g_address: String!

The way🛫!

After extracting the required information we store values in these global variables and go back to the mapViewController by dismissing the presentViewController.

let lat = json["results"][0]["geometry"]["location"]["lat"].rawString()
let lng = json["results"][0]["geometry"]["location"]["lng"].rawString()
let formattedAddress = json["results"][0]
["formatted_address"].rawString()
g_lat = lat                    
g_long = lng
g_address = formattedAddress
self.dismiss(animated: true, completion: nil)

On dismissing the search view controller we come back to map view controller. Here i display the pin on map using locateWithLong function

func locateWithLong(lon: String, andLatitude lat: String, andAddress address: String) {
DispatchQueue.main.async {
let latDouble = Double(lat)
let lonDouble = Double(lon)
self.mapView.clear()
let position = CLLocationCoordinate2D(latitude: latDouble ?? 20.0, longitude: lonDouble ?? 10.0)
let marker = GMSMarker(position: position)
let camera = GMSCameraPosition.camera(withLatitude: latDouble ?? 20.0, longitude: lonDouble ?? 10.0, zoom: 15)
self.mapView.camera = camera
self.searchButton.setTitle(address, for: .normal)
marker.map = self.mapView
}
}

Why do we use DispatchQueue here?

it’s never OK to do user interface work on the background thread

We extracted and stored coordinates using third party API calls. This all happens in background thread and not on main thread. In iOS user interface changes always happen on the main thread. If you try to implement UI changes on background thread Swift compiler throws error on runtime.

According to apple Dispatch is:

Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.

lastly we do the UI changes on the main thread and pin point the marker on the extracted location.

The below snippet provides my GoogleMapViewController class that implements the google maps.

And this way we just built a fully functional place Auto Complete iOS app.

This tutorial only showed you the basics of what the Google Maps SDK can do. There’s much more to learn; you should definitely check out the full documentation for more cool features the SDK has to offer.


hope that you have enjoyed this tutorial. If you want to read more on Google Maps SDK for iOS, write me a comment. I would be very happy to expand this tutorial with your requests.



This was my first technical story on medium. So feel free to comment, review point out any errors that might have crept in.

See you next time!! Till then enjoy iOS development📱.