Swift — Navigation App Chooser — Prompt the user to choose app to open Map Directions

This solution prompts the user to choose from the available navigation apps in the user’s iPhone, and then opens the chosen app for direction towards the given location.

Create a new Swift file named DirectionsOpts.swift and paste this entire code.

import Foundation
import UIKit
import CoreLocation
import MapKit

public enum DirectionsOpts {
    case AppleMaps
    case GoogleMaps
    case Navigon
    case Waze

    public var appname: String {
        switch self {
            case .AppleMaps:
            return "Apple Maps"
            case .GoogleMaps:
            return "Google Maps"
            case .Navigon:
            return "Navigon"
            case .Waze:
            return "Waze"
        }
    }
    public var baseUrl: String {
        switch self {
            case .AppleMaps:
            return "http://maps.apple.com"
            case .GoogleMaps:
            return "comgooglemaps://"
            case .Navigon:
            return "navigon://"
            case .Waze:
            return "waze://"
        }
    }

    public static let allApps: [DirectionsOpts] = [.AppleMaps, .GoogleMaps, .Navigon, .Waze]
    public static var availableApps: [DirectionsOpts] {
        return self.allApps.filter { app in app.available }
    }

    public var url: URL? {
        return URL(string: self.baseUrl)
    }

    public var available: Bool {
        guard let url = self.url else {
            return false
        }
        return UIApplication.shared.canOpenURL(url)
    }

    public func directionsUrlString(coordinate: CLLocationCoordinate2D, name: String = "Destination") -> String {

        var urlString = self.baseUrl

        switch self {
            case .AppleMaps:
            urlString.append("?q=\(coordinate.latitude),\(coordinate.longitude)=d&t=h")

            case .GoogleMaps:
            urlString.append("?saddr=&daddr=\(coordinate.latitude),\(coordinate.longitude)&directionsmode=driving")

            case .Navigon:
            urlString.append("coordinate/\(name)/\(coordinate.longitude)/\(coordinate.latitude)")

            case .Waze:
            urlString.append("?ll=\(coordinate.latitude),\(coordinate.longitude)&navigate=yes")
        }

        let urlwithPercentEscapes =
                urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? urlString

        return urlwithPercentEscapes
    }

    public func directionsUrl(coordinate: CLLocationCoordinate2D, name: String = "Destination") -> URL? {
        let urlString = self.directionsUrlString(coordinate: coordinate, name: name)
        return URL(string: urlString)
    }

    //open the app
    public func openWithDirections(coordinate: CLLocationCoordinate2D,
                                   name: String = "Destination",
                                   completion: ((Bool) -> Swift.Void)? = nil) {

        // Apple Maps can be opened differently than other navigation apps
        if self == .AppleMaps {
            let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: coordinate, addressDictionary:nil))
            mapItem.name = name

            let success = mapItem.openInMaps(launchOptions:
                [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving])

            completion?(success)
        }

        guard let url = self.directionsUrl(coordinate: coordinate, name: name) else {
            completion?(false)
            return
        }

        // open the app with appropriate method for your target iOS version
        if #available(iOS 10.0, *) {
            UIApplication.shared.open(url, options: [:], completionHandler: {
                (success) in
                print("Open \(url.absoluteString): \(success)")
                completion?(success)
            })
        } else {
            let success = UIApplication.shared.openURL(url)
            completion?(success)
        }
    }

    // present available apps
    public static func directionsAlertController(coordinate: CLLocationCoordinate2D,
                                                 name: String = "Destination",
                                                 title: String = "Directions Using",
                                                 message: String? = nil,
                                                 completion: ((Bool) -> ())? = nil)
            -> UIAlertController {

        let directionsAlertView = UIAlertController(title: title,
                message: nil,
                preferredStyle: .actionSheet)

        for navigationApp in DirectionsOpts.availableApps {

            let action = UIAlertAction(title: navigationApp.appname,
                    style: UIAlertAction.Style.default,
                handler: { action in
                    navigationApp.openWithDirections(coordinate: coordinate,
                            name: name,
                            completion: completion)
                })

                directionsAlertView.addAction(action)
        }

        let cancelAction = UIAlertAction(title: "Dismiss",
                style: UIAlertAction.Style.cancel,
                handler: { action in completion?(false) })

        directionsAlertView.addAction(cancelAction)

        return directionsAlertView
    }
}

Then in your ViewController, write this function, while passing the destination coordinates in the argument.

func navigateTo(coords: CLLocationCoordinate2D) {

    let nav = DirectionOpts.directionsAlertController(coordinate: coords, name: "Destination", title: "Navigate to Destination", message: "Any message") { com in
        //completion
    }
    self.present(nav, animated: true, completion: nil)

}

 

 

 

 


Also published on Medium.

By |2020-04-21T13:54:04+00:00April 21st, 2020|Categories: iOS, iOS Tutorials|Tags: |0 Comments

Leave A Comment