To draw a route between 2 coordinates you need to make a request to the Google Maps Directions API and parse its response. Therefore you firstly need to get an API key for your request. You can get one here, by creating a project and also enabling the Google Maps Directions API in this project.
Assuming you have the Google Maps SDK installed, you need to make a request to the directions API and then parse its response. Once you have parsed the response JSON, you can create a GMSPath object. I prefer to do that with a function that has two inputs, start & end CLLocationCoordinate2D objects and that returns the GMSPath on success or an error if something failed. The code below is in Swift 3.
My class and its function look like this:
import Foundation
import CoreLocation
import GoogleMaps
class SessionManager {
let GOOGLE_DIRECTIONS_API_KEY = "INSERT_YOUR_API_KEY_HERE"
func requestDirections(from start: CLLocationCoordinate2D, to end: CLLocationCoordinate2D, completionHandler: @escaping ((_ response: GMSPath?, _ error: Error?) -> Void)) {
guard let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(start.latitude),\(start.longitude)&destination=\(end.latitude),\(end.longitude)&key=\(GOOGLE_DIRECTIONS_API_KEY)") else {
let error = NSError(domain: "LocalDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to create object URL"])
print("Error: \(error)")
completionHandler(nil, error)
return
}
// Set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: url) { (data, response, error) in
// Check if there is an error.
guard error == nil else {
DispatchQueue.main.async {
print("Google Directions Request Error: \((error!)).")
completionHandler(nil, error)
}
return
}
// Make sure data was received.
guard let data = data else {
DispatchQueue.main.async {
let error = NSError(domain: "GoogleDirectionsRequest", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to receive data"])
print("Error: \(error).")
completionHandler(nil, error)
}
return
}
do {
// Convert data to dictionary.
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
DispatchQueue.main.async {
let error = NSError(domain: "GoogleDirectionsRequest", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to convert JSON to Dictionary"])
print("Error: \(error).")
completionHandler(nil, error)
}
return
}
// Check if the the Google Direction API returned a status OK response.
guard let status: String = json["status"] as? String, status == "OK" else {
DispatchQueue.main.async {
let error = NSError(domain: "GoogleDirectionsRequest", code: 3, userInfo: [NSLocalizedDescriptionKey: "Google Direction API did not return status OK"])
print("Error: \(error).")
completionHandler(nil, error)
}
return
}
print("Google Direction API response:\n\(json)")
// We only need the 'points' key of the json dictionary that resides within.
if let routes: [Any] = json["routes"] as? [Any], routes.count > 0, let routes0: [String: Any] = routes[0] as? [String: Any], let overviewPolyline: [String: Any] = routes0["overview_polyline"] as? [String: Any], let points: String = overviewPolyline["points"] as? String {
// We need the get the first object of the routes array (route0), then route0's overview_polyline and finally overview_polyline's points object.
if let path: GMSPath = GMSPath(fromEncodedPath: points) {
DispatchQueue.main.async {
completionHandler(path, nil)
}
return
} else {
DispatchQueue.main.async {
let error = NSError(domain: "GoogleDirections", code: 5, userInfo: [NSLocalizedDescriptionKey: "Failed to create GMSPath from encoded points string."])
completionHandler(nil, error)
}
return
}
} else {
DispatchQueue.main.async {
let error = NSError(domain: "GoogleDirections", code: 4, userInfo: [NSLocalizedDescriptionKey: "Failed to parse overview polyline's points"])
completionHandler(nil, error)
}
return
}
} catch let error as NSError {
DispatchQueue.main.async {
completionHandler(nil, error)
}
return
}
}
task.resume()
}
}
Then you can use that in your viewDidLoad
like that:
@IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
let sessionManager = SessionManager()
let start = CLLocationCoordinate2D(latitude: 37.778483, longitude: -122.513960)
let end = CLLocationCoordinate2D(latitude: 37.706753, longitude: -122.418677)
sessionManager.requestDirections(from: start, to: end, completionHandler: { (path, error) in
if let error = error {
print("Something went wrong, abort drawing!\nError: \(error)")
} else {
// Create a GMSPolyline object from the GMSPath
let polyline = GMSPolyline(path: path!)
// Add the GMSPolyline object to the mapView
polyline.map = self.mapView
// Move the camera to the polyline
let bounds = GMSCoordinateBounds(path: path!)
let cameraUpdate = GMSCameraUpdate.fit(bounds, with: UIEdgeInsets(top: 40, left: 15, bottom: 10, right: 15))
self.mapView.animate(with: cameraUpdate)
}
})
}
Hope you find it useful.