Search code examples
iosswiftrecursionmapkitmkdirection

Recursive Function, With Completion Block, That Retrieves Multiple MKDirections - Swift


I'm trying to retrieve an array of MKRoute that contains multiple routes all starting at the same place, but each having a different destination.

The problem is that the only way I've been able to come up with to do this is through a recursive function, but I can't find anything on how to use recursive functions with completion blocks. Since loading the routes is done Asynchronously a completion block is needed.

How do I go about getting the following functionality but with completion blocks? The "add to the return" functionality?

func factorial(of num: Int) -> Int {
    if num == 1 {
        return 1
    } else {
        return num * factorial(of:num - 1)
    }
}

Here is my function code

func getDirections(originCoordinate: CLLocationCoordinate2D, destinationCoordinates: [CLLocationCoordinate2D], completion: @escaping(_ routes:[MKRoute]?, _ error: Error?) -> Void) {

    // Origin is the same for each route, what changes is the destination
    // A singular origin coordinate with an array of destination coordinates is passed in
    // The function would in theory be recursive, returning each route from the origin to each of the destinations.

    // Leave function if no more destination coordinates are passed
    if destinationCoordinates.count == 0 {return}

    // Origin
    let originPlacemark = MKPlacemark(coordinate: originCoordinate)
    let originItem = MKMapItem(placemark: originPlacemark)

    // Destination is made from the first element of the passed in destinationCoordinates array.
    let destinationPlacemark = MKPlacemark(coordinate: destinationCoordinates.first!)
    let destinationItem = MKMapItem(placemark: destinationPlacemark)

    // Direction Request setup
    let directionRequest = MKDirections.Request()
    directionRequest.source = originItem
    directionRequest.transportType = .automobile
    directionRequest.destination = destinationItem

    let directions = MKDirections(request: directionRequest)

    // Calculating directions
    // Heart of function
    directions.calculate { (response, err) in

        // Checking if a response is returned
        guard let response = response else {
            completion(nil, err)
            return
        }

        // Response is returned
        let route = response.routes[0]

        let tail = Array.dropFirst(destinationCoordinates)

        // Recursive part that I'm not sure how to properly implement
        completion([route].append(getDirections(originCoordinate, tail)), nil)

    }
    // If no response is retrieved, our guard let statement sends us here
}

Solution

  • With a function with a completion handler, in the recursive call, you need to provide a closure to the call and then call the completion handler in that closure.

    Here is how you'd do that with factorial:

    func factorial(of num: Int, completion: (Int) -> ()) {
        if num == 1 {
            completion(1)
        } else {
            factorial(of: num - 1) { partial in
                completion(num * partial)
            }
        }
    }
    
    factorial(of: 8) { result in
        print(result)
    }
    
    40320