The Curious Case of the Apple Geocoder

A recent question on StackOverflow piqued my interest. The question was from someone who was looping through an array of coordinates and submitting them to the Apple geocoding service for reverse geocoding, and who couldn’t understand why he was consistently only getting one address returned. The comments were along the lines of it being due to Apple rate-limiting the service to stop it being spammed, which was also my immediate thought. Then a couple of things struck me that felt a bit strange:

  • even if the number of requests was above Apple’s (unpublished) threshold, I’d have expected more than just one to be reverse geocoded
  • no errors were being received. In fact no responses were being received at all for anything other than the first request. Even if Apple was classing the multiple requests as an abuse of the service I’d still expect a response: the documentation clearly states (my emphasis):

Geocoding requests are rate-limited for each app, so making too many requests in a short period of time may cause some of the requests to fail. (When the maximum rate is exceeded, the geocoder returns an error object with the CLError.Code.network error to the associated completion handler.)

Given I use Geocoding in a couple of apps I was keen to understand this behaviour, and so fired up Xcode and had a play around. My quick and dirty testing confirmed the behaviour that prompted the original question. My observations were:

  • submitting even just two consecutive requests results in the first request being serviced while the second disappears into thin air. The completion handler is not executed for the second request.
  • the Geocoder has an isGeocoding property. The docs are unclear on when or why this should be used but it seems reasonable assumptions that it will return true until the previous operation has completed, and that requests should only be made when it returns false. Building this check into my testing made no difference: I still only received a response (i.e. the completion handler was called) for first request made.

To see if it was possible to resolve multiple coordinates in a different way, I tried using the completion handler to implement an iterative approach to processing an array of coordinates:

func reverse(locs: [CLLocation]){
   guard !locs.isEmpty else {
      print("All Done")
      return
   }
   geocoder.reverseGeocodeLocation(locs.first!){ placemarks, error in
      if let error = error {
         print("Error \(error)")
      } else if let placemark = placemarks?.first {
         print("place: \(placemark)")
      }
      reverse(locs: Array(locs.dropFirst()))
   }
} 

This quite happily, and consistently, reverse geocodes an array of 20 locations without any problem at all.

So I have a working solution in case I ever need it (I probably won’t, but hey… 😀) however I’d still like to understand why either GLCeocoder or the Apple geocoding service will take in multiple requests and then just ignore any but the first. And why the above iterative approach works but using the isGeocoding flag, which I’d expect to only return true once the previous completion block had exited, doesn’t?

Anyone…?

Leave a Reply