I have a view controller with map kit integrated. I need to shoot an alert before opening that map, asking to choose from all similar applications of maps to open it with. For instance, if google maps app is installed in my iPhone, there should be an option for it, along with the default mapkit view. Is there a possibility to achieve this functionality which scans every similar app from iphone and returns the result as options to open map with.
Open the Gmail app and tap the menu icon in the top left corner of the screen. Scroll down to the bottom of the menu and tap Settings. On the Settings screen, tap the Default apps option. Under the Navigate from your location and the Navigate between location sections, tap to select Google Maps.
Swift 5+ solution based on previous answers, this one shows a selector between Apple Maps, Google Maps, Waze and City Mapper. It also allows for some optional location title (for those apps that support it) and presents the alert only if there are more than 1 option (it opens automatically if only 1, or does nothing if none).
func openMaps(latitude: Double, longitude: Double, title: String?) {
let application = UIApplication.shared
let coordinate = "\(latitude),\(longitude)"
let encodedTitle = title?.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
let handlers = [
("Apple Maps", "http://maps.apple.com/?q=\(encodedTitle)&ll=\(coordinate)"),
("Google Maps", "comgooglemaps://?q=\(coordinate)"),
("Waze", "waze://?ll=\(coordinate)"),
("Citymapper", "citymapper://directions?endcoord=\(coordinate)&endname=\(encodedTitle)")
]
.compactMap { (name, address) in URL(string: address).map { (name, $0) } }
.filter { (_, url) in application.canOpenURL(url) }
guard handlers.count > 1 else {
if let (_, url) = handlers.first {
application.open(url, options: [:])
}
return
}
let alert = UIAlertController(title: R.string.localizable.select_map_app(), message: nil, preferredStyle: .actionSheet)
handlers.forEach { (name, url) in
alert.addAction(UIAlertAction(title: name, style: .default) { _ in
application.open(url, options: [:])
})
}
alert.addAction(UIAlertAction(title: R.string.localizable.cancel(), style: .cancel, handler: nil))
contextProvider.currentViewController.present(alert, animated: true, completion: nil)
}
Note this solution uses R.swift for string localization but you can replace those with NSLocalizedString
normally, and it uses a contextProvider.currentViewController
to get the presented UIViewController
, but you can replace it with self
if you are calling this in a view controller already.
As usual, you need to also add the following to your app Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>citymapper</string>
<string>comgooglemaps</string>
<string>waze</string>
</array>
Swift 5+
Base on @Emptyless answer.
import MapKit
func openMapButtonAction() {
let latitude = 45.5088
let longitude = -73.554
let appleURL = "http://maps.apple.com/?daddr=\(latitude),\(longitude)"
let googleURL = "comgooglemaps://?daddr=\(latitude),\(longitude)&directionsmode=driving"
let wazeURL = "waze://?ll=\(latitude),\(longitude)&navigate=false"
let googleItem = ("Google Map", URL(string:googleURL)!)
let wazeItem = ("Waze", URL(string:wazeURL)!)
var installedNavigationApps = [("Apple Maps", URL(string:appleURL)!)]
if UIApplication.shared.canOpenURL(googleItem.1) {
installedNavigationApps.append(googleItem)
}
if UIApplication.shared.canOpenURL(wazeItem.1) {
installedNavigationApps.append(wazeItem)
}
let alert = UIAlertController(title: "Selection", message: "Select Navigation App", preferredStyle: .actionSheet)
for app in installedNavigationApps {
let button = UIAlertAction(title: app.0, style: .default, handler: { _ in
UIApplication.shared.open(app.1, options: [:], completionHandler: nil)
})
alert.addAction(button)
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(cancel)
present(alert, animated: true)
}
Also put these in your info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
<string>waze</string>
</array>
Cheers!
You can create an array of checks to map the installed apps using sumesh's answer [1]:
var installedNavigationApps : [String] = ["Apple Maps"] // Apple Maps is always installed
and with every navigation app you can think of:
if (UIApplication.sharedApplication().canOpenURL(url: NSURL)) {
self.installedNavigationApps.append(url)
} else {
// do nothing
}
Common navigation apps are:
A lot more can be found at: http://wiki.akosma.com/IPhone_URL_Schemes
After you created your list of installed navigation apps you can present an UIAlertController:
let alert = UIAlertController(title: "Selection", message: "Select Navigation App", preferredStyle: .ActionSheet)
for app in self.installNavigationApps {
let button = UIAlertAction(title: app, style: .Default, handler: nil)
alert.addAction(button)
}
self.presentViewController(alert, animated: true, completion: nil)
Of course you need to add the behavior of a button click in the handler with the specified urlscheme. For example if Google Maps is clicked use something like this:
UIApplication.sharedApplication().openURL(NSURL(string:
"comgooglemaps://?saddr=&daddr=\(place.latitude),\(place.longitude)&directionsmode=driving")!) // Also from sumesh's answer
With only Apple Maps and Google Maps installed this will yield something like this:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With