Google Maps iOS SDK Tutorial: Getting Started
In this tutorial, you’ll learn how to use the Google Maps iOS SDK to make an app that searches for nearby places to eat, drink or shop for groceries. By Ron Kliffer.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Google Maps iOS SDK Tutorial: Getting Started
30 mins
- Getting Started
- Creating the API Key
- Adding the SDK
- Creating the Map View
- Adding a UIView
- Adding the Constraints for the Map View
- Creating an Outlet for the Map View
- Getting the Location
- Adding MapViewController Extension
- Creating an Instance of CLLocationManager
- Asking for Permission to Get Location
- Showing the Street Address
- Creating the UI for the Address
- Getting Address From a Coordinate
- Updating the Address
- Fixing the Street Address UI
- Finding Places
- Marking Locations on the Map
- Searching for Nearby Places
- Adding a Refresh Map Option
- Showing Additional Place Information
- Tidying up the UI
- Google Maps Versus Apple MapKit
- Advantages of Google Maps iOS SDK
- Advantages of Apple’s MapKit
- Where to Go From Here?
Updating the Address
You’ll want to call the method above every time the user changes position on the map. To do so, you’ll use GMSMapViewDelegate
.
Add another extension to the bottom of MapViewController.swift as follows:
// MARK: - GMSMapViewDelegate
extension MapViewController: GMSMapViewDelegate {
}
This will declare that MapViewController
conforms to GMSMapViewDelegate
.
Next, add the following line of code to viewDidLoad()
:
mapView.delegate = self
This makes MapViewController
the map view’s delegate.
Finally, add the following method to the newly-added extension:
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
reverseGeocode(coordinate: position.target)
}
This method is called each time the map stops moving and settles in a new position. You then make a call to reverse geocode the new position and update the addressLabel
‘s text.
Build and run. You’ll see the address of your current location — real or simulated — at the bottom of the screen.
Notice anything wrong with this picture?
[spoiler]The Google logo and Locate button are hiding behind the label![/spoiler]
Fixing the Street Address UI
Fortunately, GMSMapView
provides a very simple solution for this: padding. When you apply padding to the map, all of the visual elements will be placed according to that padding.
Head back to reverseGeocodeCoordinate(_:)
and add these lines just before the animation block:
// 1
let labelHeight = self.addressLabel.intrinsicContentSize.height
let topInset = self.view.safeAreaInsets.top
self.mapView.padding = UIEdgeInsets(
top: topInset,
left: 0,
bottom: labelHeight,
right: 0)
Now, replace the animation block with:
UIView.animate(withDuration: 0.25) {
//2
self.pinImageVerticalConstraint.constant = (labelHeight - topInset) * 0.5
self.view.layoutIfNeeded()
}
This does two things:
- Adds padding to the top and bottom of the map before the animation block. The top padding equals the view’s top safe area inset, while the bottom padding equals the label’s height.
- Updates the location pin’s position to match the map’s padding by adjusting its vertical layout constraint.
Build and run again. This time, the Google logo and locate button move to their new position once the label becomes visible.
Move the map around; you’ll notice that the address changes every time the map settles to a new position. You’ll add animation to dampen this effect.
Add the following method to the GMSMapViewDelegate
extension:
func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
addressLabel.lock()
}
This method is called every time the map starts to move. It receives a Bool
that tells you if the movement originated from a user gesture, such as scrolling the map, or if the movement originated from code.
If the movement came from the user, you call lock()
on addressLabel
to give it a loading animation.
When there’s a lock()
, there must also be a corresponding unlock()
. Add the following as the first line of the closure passed to reverseGeocode(coordinate:)
. It should come right before the guard let
:
self.addressLabel.unlock()
lock()
and unlock()
, check out UIView+Extensions.swift.Build and run. As you scroll the map, you’ll see a loading animation on the address label.
Finding Places
Now that you’ve set up the map and have the user’s location in hand, it’s time to get this user fed!
You’ll use Google Places API to search for places to eat and drink around the user’s location. This is a free web service API you can use to query to find establishments, geographic locations or other points of interest near any given point.
Marking Locations on the Map
Google Maps iOS SDK provides you with the GMSMarker
class to mark locations on a map. Each marker object holds a coordinate and an icon image and renders on the map when you add it.
For this app, you’ll provide additional information on each marker. To do this, you’ll create a subclass of GMSMarker
.
Create a new Cocoa Touch Class, name it PlaceMarker and make it a subclass of GMSMarker. Ensure you choose Swift as the language for this file.
Replace the contents of PlaceMarker.swift with the following:
import UIKit
import GoogleMaps
class PlaceMarker: GMSMarker {
// 1
let place: GooglePlace
// 2
init(place: GooglePlace, availableTypes: [String]) {
self.place = place
super.init()
position = place.coordinate
groundAnchor = CGPoint(x: 0.5, y: 1)
appearAnimation = .pop
var foundType = "restaurant"
let possibleTypes = availableTypes.count > 0 ?
availableTypes :
["bakery", "bar", "cafe", "grocery_or_supermarket", "restaurant"]
for type in place.types {
if possibleTypes.contains(type) {
foundType = type
break
}
}
icon = UIImage(named: foundType+"_pin")
}
}
Here’s what this code is doing:
- Adds a property of type
GooglePlace
to thePlaceMarker
. - Declares a new designated initializer that accepts a
GooglePlace
and available location types. Fully initializes aPlaceMarker
with a position, icon image and anchor for the marker’s position as well as an appearance animation.
Searching for Nearby Places
Next, add two more properties to MapViewController
:
let dataProvider = GoogleDataProvider()
let searchRadius: Double = 1000
You’ll use dataProvider
, defined in GoogleDataProvider.swift, to make calls to the Google Places Web API. And you’ll use searchRadius
to set the distance from the user for the search. You’ve set it to 1,000 meters.
Add the following method to MapViewController
:
func fetchPlaces(near coordinate: CLLocationCoordinate2D){
// 1
mapView.clear()
// 2
dataProvider.fetchPlaces(
near: coordinate,
radius:searchRadius,
types: searchedTypes
) { places in
places.forEach { place in
// 3
let marker = PlaceMarker(place: place, availableTypes: self.searchedTypes)
// 4
marker.map = self.mapView
}
}
}
In this method, you:
- Clear the map of all markers.
- Use
dataProvider
to query Google for nearby places within thesearchRadius
, filtered to the user’s selected types. - Iterate through the results returned in the completion closure and create a
PlaceMarker
for each result. - Set the marker’s map. This line of code is what tells the map to render the marker.
Here’s the $64,000 question: When do you want to call this method?
The answer is: When the app launches. Your user will expect to see some places to eat when they open an app called “Feed Me”!
Locate locationManager(_:didUpdateLocations:)
and add the following line of code at the end:
fetchPlaces(near: location.coordinate)
Next, because the user has the ability to change the types of places to display on the map, you’ll update the search results if the selected types change.
To do this, locate typesController(_:didSelectTypes:)
and add the following line of code to the end:
fetchPlaces(near: mapView.camera.target)
Finally, you’ll give the user the option to fetch new places when their location changes.