fbpx
Hero Illustration
0 Comments
Google Maps, iOS, MapKit, Mitrais, Software Development

Exploring MapKit and Google Maps SDK in iOS

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

Contact us to learn more!

Please complete the brief information below and we will follow up shortly.

    ** All fields are required
    Leave a comment