My goal here is to have two users communicating with each other, one of which is coming towards the other one (for the sake of simplicity we'll call moovy the moving one and waity the one's who's waiting). Moovy needs to keep updated waity on the amount of time and distance left to reach him.
Since waity might actually be waiting not only him but more than one user, I preferred to leave the calculation of the remaining time and distance on moovy's device and keep track of it on firebase's realtime database, while waity's device will be listening on a list of 'incoming users'
To update firebase's real time database I put the 'calculating' login within the locationManager(didUpdateLocation)
So my function looks something like this
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let directionsRequest: MKDirections.Request = MKDirections.Request()
directionsRequest.source = MKMapItem(placemark: MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: coordinates.latitude, longitude: coordinates.longitude), addressDictionary: nil))
directionsRequest.destination = MKMapItem(placemark: MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: destination, longitude: destination.longitude), addressDictionary: nil))
directionsRequest.requestsAlternateRoutes = true
directionsRequest.transportType = .automobile
let directions = MKDirections(request: directionsRequest)
var approxTime: Double = 9999
directions.calculate { [self] (response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
if response.routes.count > 0 {
let route = response.routes[0]
approxTime = route.expectedTravelTime
viewModel.updateDestination(destinationUserId: destination.userId, currentUsername: userStore.userData.username, currentUserLocation: locations.last!, approxTime: approxTime, distance: distanceFromChosen ?? 0) { result in
print(result)
}
}
}
Where destination
is Waity's location and id, viewModel
is in charge of updating the real time database with Moovy's location, distance and time left to the arrival.
It works pretty fine for the first 10 seconds or so and then it proceeds to stop updating the time and distance left due to a max calls limit reached on the calculate
method
Specifically
Error: Error Domain=MKErrorDomain Code=3 "Directions Not Available" UserInfo={NSLocalizedFailureReason=Route information is not available at this moment., MKErrorGEOError=-3, MKErrorGEOErrorUserInfo={details = ({ intervalType = short; maxRequests = 50; "throttler.keyPath" = "app:com.app.bundleid/0x20200/short(default/any)";timeUntilReset = 33; windowSize = 60; }); timeUntilReset = 33; }, MKDirectionsErrorCode=3, NSLocalizedDescription=Directions Not Available}
Is this the right way to implement the goal I'm trying to achieve. Should I just limit the number of calls with some wait signal or should I use another approach?
Generally I'd say that getting throttled by a rate-limited API like this is a good clue that you should consider a different approach. It’s not explicit in the documentation, but it seems logical that the MKDirections
API is intended for user-initiated actions like getting directions to a location after searching in a mapping app. That's way less demanding than what your code is trying to do under the covers in the location manager delegate callback.
One could try to evade the rate limit by self-throttling the calculate()
calls as you mentioned, but that would be very fragile and unlikely to produce the real-time behavior you want. Note how the MKDirections documentation is deliberately vague on exactly what the rate limit algorithm is, so it could change in any new OS release. (The clues in the error dictionary are interesting but not official API.) Whatever it is, surely Apple intends to calibrate it to support presenting directions to a user and not real-time route recalculation.