Search code examples
swiftmapkitxcode8mkdirection

Cannot wait for the result of MKDirections.calculate, getting nil instead of it


I would like to calculate the real walking distance between my current location and a list of CLLocations using MKDirections.calculate. However, for some reason the return command at the end of the function does not wait for the result and tries to return the empty variable. My code looks like this:

func getDistance (location1: CLLocation, location2: CLLocation) {
    let coordinates1 = location1.coordinate
    let placemark1 = MKPlacemark(coordinate: coordinates1)
    let sourceItem = MKMapItem(placemark: placemark1)
    let coordinates2 = location2.coordinate
    let placemark2 = MKPlacemark(coordinate: coordinates2)
    let destinationItem = MKMapItem(placemark: placemark2)

    let request = MKDirectionsRequest()
    request.source = sourceItem
    request.destination = destinationItem
    request.requestsAlternateRoutes = true
    request.transportType = .walking

    var distance: Double?

    let directions = MKDirections(request: request)
    directions.calculate { (response, error) in

        if var routeResponse = response?.routes {
            routeResponse.sort(by: {$0.expectedTravelTime < $1.expectedTravelTime})
            let quickestRoute: MKRoute = routeResponse[0]
            distance = Double(quickestRoute.distance)
        }
    }

    return distance //returns nil
}

And after that I would like to use the function in a code like this:

let myLocation = CLLocation(latitude: 47.0, longitude: 17.0)
let destinationArray = [CLLocation(latitude: 47.1, longitude: 17.1), CLLocation(latitude: 47.2, longitude: 17.2), CLLocation(latitude: 47.3, longitude: 17.3)]
var distanceArray: [Double] = []
for destination in destinationArray {
    distanceArray.append(getDistance(location1: myLocation, location2: destination))
}
return distanceArray

I have tried closures, but they did not work because I could not find a way to return distanceArray (the same error, it did not wait for the closure to run and returned the empty array). I have also tried DispatchGroups but they had no effect (maybe I implemented them in the wrong way).

I would really appreciate your help.

Thank you.


Solution

  • Using MapKit & Swift 5

    Calculate distance between two location location, It will help anyone

    Sample Function : I have tested in Google Map as well as Apple Map

            let startLocation : CLLocation = CLLocation.init(latitude: 23.0952779, longitude: 72.5274129)
            let endLocation : CLLocation = CLLocation.init(latitude: 23.0981711, longitude: 72.5294229)
            let distance = startLocation.distance(from: endLocation)
            self.getDistance(departureDate: Date().adjust(hour: 8, minute: 0, second: 0, day: 0, month: 0), arrivalDate: Date().adjust(hour: 8, minute: 10, second: 0, day: 0, month: 0), startLocation: startLocation, endLocation: endLocation) { (distanceInMeters) in
    
                print("fake distance: \(distance)")
                let fakedistanceInMeter = Measurement(value: distance, unit: UnitLength.meters)
                let fakedistanceInKM = fakedistanceInMeter.converted(to: UnitLength.kilometers).value
                let fakedistanceInMiles = fakedistanceInMeter.converted(to: UnitLength.miles).value
                print("fakedistanceInKM :\(fakedistanceInKM)")
                print("fakedistanceInMiles :\(fakedistanceInMiles)")
    
    
                print("actualDistance : \(distanceInMeters)")
    
                let distanceInMeter = Measurement(value: distanceInMeters, unit: UnitLength.meters)
                let distanceInKM = distanceInMeter.converted(to: UnitLength.kilometers).value
                let distanceInMiles = distanceInMeter.converted(to: UnitLength.miles).value
                print("distanceInKM :\(distanceInKM)")
                print("distanceInMiles :\(distanceInMiles)")
            }
    
    

    Use of functions

                        self.getDistance(departureDate: trip.departure.dateTime, arrivalDate: trip.arrival.dateTime, startLocation: startLocation, endLocation: endLocation) { (actualDistance) in
                            print("actualDistance : \(actualDistance)")
                        }
    
    

    I am improved above function and added code here, I hope it will help someone.

    func calculateDistancefrom(departureDate: Date, arrivalDate: Date, sourceLocation: MKMapItem, destinationLocation: MKMapItem, doneSearching: @escaping (_ distance: CLLocationDistance) -> Void) {
    
            let request: MKDirections.Request = MKDirections.Request()
    
            request.departureDate = departureDate
            request.arrivalDate = arrivalDate
    
            request.source = sourceLocation
            request.destination = destinationLocation
    
            request.requestsAlternateRoutes = true
            request.transportType = .automobile
    
            let directions = MKDirections(request: request)
            directions.calculate { (directions, error) in
                if var routeResponse = directions?.routes {
                    routeResponse.sort(by: {$0.expectedTravelTime <
                        $1.expectedTravelTime})
                    let quickestRouteForSegment: MKRoute = routeResponse[0]
    
                    doneSearching(quickestRouteForSegment.distance)
                }
            }
        }
    
        func getDistance(departureDate: Date, arrivalDate: Date, startLocation : CLLocation, endLocation : CLLocation, completionHandler: @escaping (_ distance: CLLocationDistance) -> Void) {
    
            let destinationItem =  MKMapItem(placemark: MKPlacemark(coordinate: startLocation.coordinate))
            let sourceItem      =  MKMapItem(placemark: MKPlacemark(coordinate: endLocation.coordinate))
            self.calculateDistancefrom(departureDate: departureDate, arrivalDate: arrivalDate, sourceLocation: sourceItem, destinationLocation: destinationItem, doneSearching: { distance in
                completionHandler(distance)
            })
        }