MKMapItem.openInMaps() displays place mark exactly 50% of the time

1.5k views Asked by At

I've found that running some code to display a location in maps view MKMapItem.openInMaps() only works exactly 50% of the time.
In fact it precisely alternates between the MKPlacemark being displayed and it not being displayed.

For example every 1st, 3rd, 5th, 7th ...nth time the code runs then it displays the place mark, but every 2nd, 4th, 6th, 8th ...mth time it runs, the place mark is not displayed.

This is 100% reproducible running the code posted below.
This seems like its a bug, but if so then I'm surprised its not been reported nor fixed previously. But given the fact the failures are precisely alternating between success and failure leads me to think there's something else going on, hence I'm posting here to see if anybody is familiar with this issue or there is something one is supposed to do which is missing from the code, or there is a workaround:

override func viewDidAppear(_ animated: Bool) {
    displayMap()
}

func displayMap()
{
    let geoCoder = CLGeocoder()
    geoCoder.geocodeAddressString("1 Infinite Loop, Cupertino,California") { (placemark: [CLPlacemark]?, error: Error?) -> Void in
        if error == nil
        {
            if let placemark = placemark, placemark.count > 0
            {
                let location            = placemark.first
                let latitude            = (location?.location?.coordinate.latitude)!
                let longitude           = (location?.location?.coordinate.longitude)!
                let coordinates         = CLLocationCoordinate2DMake(latitude, longitude)

                let regionDistance:CLLocationDistance = 100000
                let regionSpan = MKCoordinateRegionMakeWithDistance(coordinates, regionDistance, regionDistance)

                let options = [
                    MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: regionSpan.center),
                    MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span)
                ]
                let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil)
                let mapItem = MKMapItem(placemark: placemark)

                mapItem.name = "Apple"
                mapItem.phoneNumber = "(405) 123-4567"
                mapItem.openInMaps(launchOptions: options)
            }
        }
        else
        {
            assert(false, "Unable to geocode")
        }
    }
}

This is what the result looks like when the code is run for the first, third, fifth, seventh ... time

Odd

And this is the result when the code is run for the second, fourth, sixth, eighth... time

Even

Notice in the failure screenshot that not only is the place mark not displayed on the map but the slide up is also empty.

(Currently observing this on 10.2 but have seen it on other versions too)

2

There are 2 answers

4
SwiftArchitect On BEST ANSWER

Disclaimer

Yes, there seem to be a bug in mapItem.openInMaps, which opens driving directions as an overlay.

As accurately described by @return0:

...I tried your code and the problem lies in the fact that once you have the location centered and showing, if you don't dismiss it and launch the app again it will not show...

More Oddities

  1. The second time around, mapItem.openInMaps does dismiss the driving directions overlay (instead showing the new location)
  2. If the user closes said overlay, Map is no longer confused (*)

(*) A further oddity to that situation is that once that overlay is closed, the location disappears.

Bug? → Workaround!

Force the directions overlay to go away by requesting more than one location. In practice, it means invoking Maps with twice the same location:

MKMapItem.openMaps(with: [mapItem, mapItem], launchOptions: options)

no directions, but not stuck directions but not stuck

Workaround bonus

Even if the user taps on the anchor and requests driving direction, subsequent invocation to Maps from your application to that or a different location will not leave it hanging. In other words, it works as expected.


Complete Swift 3 Source Code

func displayMap(_ address:String, name:String, tph:String)
{
    let geoCoder = CLGeocoder()
    geoCoder.geocodeAddressString(address) { (placemark: [CLPlacemark]?, error: Error?) -> Void in
        assert(nil == error,  "Unable to geocode \(error)")
        if error == nil
        {
            if let placemark = placemark, placemark.count > 0
            {
                let location            = placemark.first
                let latitude            = (location?.location?.coordinate.latitude)!
                let longitude           = (location?.location?.coordinate.longitude)!
                let coordinates         = CLLocationCoordinate2DMake(latitude, longitude)

                let regionDistance:CLLocationDistance = 10000
                let regionSpan = MKCoordinateRegionMakeWithDistance(coordinates, regionDistance, regionDistance)

                let options = [
                    MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: regionSpan.center),
                    MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span)
                ]
                let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil)
                let mapItem = MKMapItem(placemark: placemark)

                mapItem.name = name
                mapItem.phoneNumber = tph
                MKMapItem.openMaps(with: [mapItem, mapItem], launchOptions: options)
            } else {
                print("Something wrong with \(placemark)")
            }
        }
    }
}

...and invocation

@IBAction func doApple() {
    displayMap("1 Infinite Loop, Cupertino, California", name: "Apple", tph: "(408) 996–1010")
}

@IBAction func doMicrosoft() {
    displayMap("One Microsoft Way, Redmond, WA", name: "Microsoft", tph: "1-800-MICROSOFT")
}

@IBAction func doIBM() {
    displayMap("1 New Orchard Road. Armonk, New York", name: "IBM", tph: "(914) 499-1900")
}
0
Andrey Gordeev On

On iOS 10.3.2 this issue still persists for me. However, this workaround is making mapItem not to disappear:

MKMapItem.openMaps(with: [mapItem], launchOptions: options)