L_Cleo
L_Cleo

Reputation: 1527

Constantly retrieving time to destination MapKIt

My goal here to have two users, one of which walking/biking/driving towards the other, and let the other user check out the remaining time and distance for the other user to arrive. To achieve this I'm currently using FIR Realtime Database.

I figured I could use the MKDirections.Request to calculate the remaining time. But of course the way I implemented it:

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

    directionsRequest.source = MKMapItem(placemark: MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: coordinates.latitude, longitude: coordinates.longitude), addressDictionary: nil))
    directionsRequest.destination = MKMapItem(placemark: MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: destination.latitude, longitude: destination.longitude), addressDictionary: nil))
                
                
    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
            }
         }
     }
}

where diretionsRequest is defined as

let directionsRequest: MKDirections.Request = MKDirections.Request()

init() {
    directionsRequest.requestsAlternateRoutes = true
    directionsRequest.transportType = .automobile
}

As you can probably immagine this implementation fails after some time for maxing out the request attempts, and returning the following error

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:someapp/0x20200/short(default/any)"; timeUntilReset = 31; windowSize = 60; } ); timeUntilReset = 31; }, MKDirectionsErrorCode=3, NSLocalizedDescription=Directions Not Available}

I could limit the call to that function to once every 5 seconds, but seeing apple limited the requests I don't think I'm on the right path here. Is there any better way to implement this? Maybe with some other object/function that I didn't manage to find

SN: Currently using SwiftUI 5.5

EDIT: I'm adding this small comment to clarify it, the problem is that

directions.calculate

cannot be called many times, I think 50 requests per 30 seconds. But the other users update the location every second or so on fir realtime database, so this means that each calculation is made every 1 second, and this maxes out the calculation requests.

Upvotes: 0

Views: 234

Answers (1)

xTwisteDx
xTwisteDx

Reputation: 2472

The best way to do this, in my opinion, is to resolve using Firestores built in realtime update functionality. Google provides an example snippet that looks like this. https://firebase.google.com/docs/firestore/query-data/listen#swift

db.collection("cities").document("SF")
    .addSnapshotListener { documentSnapshot, error in
      guard let document = documentSnapshot else {
        print("Error fetching document: \(error!)")
        return
      }
      guard let data = document.data() else {
        print("Document data was empty.")
        return
      }
      print("Current data: \(data)")
    }

Essentially what's happening here is everytime the document SF is updated, there is a new snapshot that is triggered in that closure. What's especially cool about it is that you can use this to notify the receiving party of an update to the database. So you'd also want to configure the document to have some field lat and some field long where the snapshot listener is watching for those updates. When an update happens, then calculate your distance. Doing it this way you only ever calculate whenever a new value is created on the database, also you can limit how often your app creates a new value to the database. All you need to do is make sure that each person is looking at the proper document with the values you're expecting.

Another tip would be to have both users, reporting to the same document. If you do that, it becomes even easier because a snapshotListener will listen for local changes first, allowing you to calculate your distance anytime you write to the document, then when it's received the other party can get their updates as well. Yours might look something like this.

db.collection("yourTripIdentifer").document("yourGroupIdentifier")
    .addSnapshotListener { documentSnapshot, error in
      guard let document = documentSnapshot,
            let data =  document.data() else { return }

      //We have data, let's use it.
      calculateDistance(from: data)
    }

Upvotes: 0

Related Questions