In today’s world, almost everyone relies on various applications to assist them with everyday needs, for example, getting directions, ordering food, and buying goods and services online. Mapping software helps to make the UI of these applications much more interactive.
There are two ways to create maps in an iOS application:
- Using MapKit
- Or using Google Maps SDK
This article will discuss the differences between MapKit and Google Maps SDK.
Installation
MapKit requires no registration, and for installation, go directly to your project and import MapKit into your top-level code. Google Maps SDK has many installation ways, but we will use CocoaPods. Go to this link to install using CocoaPods.
We will show you how to develop a map application with the following features:
- Location search
- Show polyline
- Calculate distance and duration
Implementation
Search Location Function
On MapKit, to create search functions, we need to use UISearchController and modify the searchResultController in another class called LocationSearchTable.
In the LocationSearchTable class, there is an extension to handle user input as naturalLanguageQuery and run the MKLocalSearch.Request(). This class is inherited UITableViewController, so the results of placemark data will show on the UITableViewCell.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
@IBOutlet var mapView: MKMapView!
var resultSearchController:UISearchController? = nil
override func viewDidLoad() {
super.viewDidLoad()
//initilize search bar
let locationSearchTable = storyboard!.instantiateViewController(withIdentifier: "LocationSearchTable") as! LocationSearchTable
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController?.searchResultsUpdater = locationSearchTable
let searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Search for places"
navigationItem.searchController = resultSearchController
resultSearchController?.hidesNavigationBarDuringPresentation = false
definesPresentationContext = true
//initialize table view for result of placemark
locationSearchTable.mapView = mapView
locationSearchTable.handleMapSearchDelegate = self
//initialize mapkit
mapView.delegate = self
}
LocationSearchTable.swift
import UIKit
import MapKit
class LocationSearchTable: UITableViewController {
var matchingItems:[MKMapItem] = []
var mapView: MKMapView? = nil
var handleMapSearchDelegate:HandleMapSearch? = nil
}
extension LocationSearchTable: UISearchResultsUpdating{
func updateSearchResults(for searchController: UISearchController) {
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = searchBarText
request.region = mapView.region
let search = MKLocalSearch(request: request)
. . .
}
}
extension LocationSearchTable {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
. . .
return cell
}
}
extension LocationSearchTable {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedItem = matchingItems[indexPath.row].placemark
handleMapSearchDelegate?.dropPinZoomIn(placemark: selectedItem)
dismiss(animated: true, completion: nil)
}
}
Next, we will implement the same functionality using Google Maps SDK, which will be simpler due to the GMSAutocompleteResultsViewController() function which will provide an interface that displays place auto-complete predictions in a table view. The table view will be updated automatically as input text changes.
import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation
class ViewController: UIViewController {
var locationManager: CLLocationManager!
var resultsViewController: GMSAutocompleteResultsViewController?
var searchController: UISearchController?
var resultView: UITextView?
var currentLocation: CLLocation?
var placesClient: GMSPlacesClient!
@IBOutlet var mapView: GMSMapView!
override func viewDidLoad()
//searchbox
resultsViewController = GMSAutocompleteResultsViewController()
resultsViewController?.delegate = self
searchController = UISearchController(searchResultsController: resultsViewController)
searchController?.searchResultsUpdater = resultsViewController
searchController?.searchBar.sizeToFit()
navigationItem.titleView = searchController?.searchBar
// When UISearchController presents the results view, present it in
// this view controller, not one further up the chain.
definesPresentationContext = true
// Prevent the navigation bar from being hidden when searching.
searchController?.hidesNavigationBarDuringPresentation = false
placesClient = GMSPlacesClient.shared()
}
}
In MapKit, we need to manually create a table view cell to store the list of placemark data. Still, in Google Maps SDK, we don’t need to make the UI because it already exists in the GMSAutocompleteResultsViewController function.
Show Polyline Function
On MapKit, there is an MKDirections. You can request the object to provide directions using two coordinates source and destination. There is also a response to the request for directions and variables to draw them on maps using RouteFirst Polyline.
func createEstimate(_ from: CLLocationCoordinate2D, _ to: CLLocationCoordinate2D){
let req = MKDirections.Request()
req.source = MKMapItem(placemark: MKPlacemark(coordinate: from))
req.destination = MKMapItem(placemark: MKPlacemark(coordinate: to))
let directions = MKDirections(request: req)
directions.calculate { (response, error) in
if error != nil {
let ac = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
self.renderPolyline(response!)
self.showEstimatedTime(response!)
}
}
Next, we will implement the same functionality using Google Maps SDK, which will be more complex because we need to request the directions API from Google using the latitude and longitude of both the source and destination as parameters and the Google API key. It will return a JSON file, and we must parse the JSON to receive the object name “overview polyline.” Based on that, we can show the polyline using GMSPath.init object and “points” as a parameter.
func fetchRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D) {
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(source.latitude),\(source.longitude)&destination=\(destination.latitude),\(destination.longitude)
&sensor=false&mode=driving&key=\(googleApiKey)")!
URLSession.shared.dataTask(with: url, completionHandler: {
(data, response, error) in
if(error != nil){
print("error")
}else{
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String : AnyObject]
let routes = json["routes"] as! NSArray
OperationQueue.main.addOperation({
for route in routes {
let routeOverviewPolyline:NSDictionary = (route as! NSDictionary).value(forKey: "overview_polyline") as! NSDictionary
let points = routeOverviewPolyline.object(forKey: "points")
self.drawPath(from: points as! String)
}
})
} catch let error as NSError{
print("error:\(error)")
}
}
}).resume()
}
func drawPath(from polyStr: String){
let path = GMSPath.init(fromEncodedPath: polyStr)
let polyline = GMSPolyline.init(path: path)
polyline.strokeWidth = 3
polyline.strokeColor = .orange
polyline.map = self.mapView
In MapKit, we don’t need to request other APIs because the Apple Server already provides it, but in Google Maps SDK, we need to create one adding one more process to get polyline.
Calculating Distance and Duration
Using response.routes.first.ExpectedTravelTime method on MapKit will return an answer giving the distance and duration. The expected time and distance assume ideal conditions.
Showing the distance and duration on Google Maps SDK by calling the directions API and decoding the return JSON file. It needs to loop the data object named “routes” and find the data object called “duration” and “distance.”
func showEstimatedTime(_ response: MKDirections.Response){
let seconds = response.routes.first?.expectedTravelTime
let (h,m,s) = self.secondsToHoursMinutesSeconds(Int(seconds!))
let distance = response.routes.first?.distance
let km = distance! / 1000
let ac = UIAlertController(title: "Estimated", message: "\(String(format:"%.01f", km)) Kilometers \n \(h) Hours, \(m) Minutes, \(s) Seconds", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
Next, we will implement the same functionality using Google Maps SDK. Both MapKit and Google Maps SDK has the same way of showing duration and distance, but MapKit does not require an API request as Google Maps does.
func fetchRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D) {
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(source.latitude),\(source.longitude)&destination=\(destination.latitude),\(destination.longitude)&sensor=false&mode=driving&key=\(googleApiKey)")!
URLSession.shared.dataTask(with: url, completionHandler: {
(data, response, error) in
if(error != nil){
print("error")
}else{
DispatchQueue.main.async {
self.mapView.clear(
}
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String : AnyObject]
let routes = json["routes"] as! NSArray
OperationQueue.main.addOperation({
for route in routes {
if let legs = (route as AnyObject) as? [String : AnyObject]{
let legs = legs["legs"] as! [[String : AnyObject]]
for leg in legs {
let durations:NSDictionary = (leg as NSDictionary).value(forKey: "duration") as! NSDictionary
let textDuration = durations.object(forKey: "text")
let distance:NSDictionary = (leg as NSDictionary).value(forKey: "distance") as! NSDictionary
let textDistance = distance.object(forKey: "text")
self.showEstimate(distance: textDistance as! String, duration: textDuration as! String)
let end_location: NSDictionary = (leg as NSDictionary).value(forKey: "end_location") as! NSDictionary
let textLat = end_location.object(forKey: "lat")
let textLong = end_location.object(forKey: "lng")
self.showPinPoint(latitude: textLat as! Double, longitude: textLong as! Double)
}
}
let routeOverviewPolyline:NSDictionary = (route as! NSDictionary).value(forKey: "overview_polyline") as! NSDictionary
let points = routeOverviewPolyline.object(forKey: "points")
self.drawPath(from: points as! String)
}
})
} catch let error as NSError{
print("error:\(error)")
}
}
}).resume()
}
func drawPath(from polyStr: String){
let path = GMSPath.init(fromEncodedPath: polyStr)
let polyline = GMSPolyline.init(path: path)
polyline.strokeWidth = 3
polyline.strokeColor = .orange
let bounds = GMSCoordinateBounds(path: path!)
self.mapView!.animate(with: GMSCameraUpdate.fit(bounds, withPadding: 50))
self.mapView!.padding = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
polyline.map = self.mapView
}
func showEstimate(distance jarak: String, duration: String){
let ac = UIAlertController(title: "Estimated", message: "\(jarak)\n\(duration)", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
Here’s the complete code of MapKit and Google Maps.
After we provide examples of the functions of both MapKit and Google Maps SDK, we will provide an explanation of prices and other features:
Price
MapKit is entirely free. We can implement the 3 features that we have demonstrated above without any given limit. Google Maps SDK is free if it is within the limit of user requests regarding the 3 features above that are given per month. Google gives us $200 of free usage each month. You must also open a billing account using your credit card to get the API KEY. Follow this link for further information about the pricing of the Google Maps SDK.
Features
MapKit can draw the polyline, get direction and duration, and show the result of searching for places.
Google Maps SDK has more features than MapKit. For example, the search feature can show the place, multiple endpoints, the full address and type, a phone number, and the place’s website. You would probably have noticed that Google Maps SDK is also available for Android, web, and Flutter platforms.
MapKit and Google Maps SDK in Action
A comparison of an iOS Simulator from iPhone 12 Pro 14.1.
Mapkit
Google Maps SDK
Application Size
We are using an actual device (iPhone 8 iOS 16.0) to check the application size of both maps. Google Maps SDK results in a larger application size because additional files, such as Google Places, are imported during application initialization. On the other hand, MapKit has a smaller application size because there are no extra files.
We can use the comparison table below to help decide which one is the better solution based on the requirement.
All of the features that we have described above are implemented on these hardware and software:
- Mac Mini 8GB 2018–6 Core i5
- XCode Version 14.0 (14A309)
MapKit | Google Maps SDK | |
Price | Free | $200 |
Installation | Built-in | Multiple methods (CocoaPods recommended) |
Application Size | Using Google Map SDK will result in larger application file size (when compared to using MapKit) | |
Features | Polyline Marker Placemark Distance Duration | Polyline Marker Placemark Distance Duration Multiple Point Contacts Review Rating Price Level |
Platform | Only on iOS | Multi-platform |
Memory Usage | MapKit has a higher memory usage than Google Maps SDK when idle. | Google Maps SDK consumes higher memory when doing a place search. |
We have already given some examples of functions and differences for both MapKit and Google Maps SDK. So, if you would like to create a smaller application to show maps, draw polylines, and get distance and direction on iOS, you should use MapKit because it’s lightweight. Otherwise, if you need to create an extensive integrated maps application, get the details of the place (e.g., contacts, reviews, or price level), make navigation, and have multiple endpoints, not just for iOS. We’d recommend the Google Maps SDK.
In summary, it is a question of choosing the correct “Horses for Courses”!
Author: Erik Satriawan, Software Engineer Junior Programmer & Medianto Laksmono, Software Engineer Analyst Programmer