Closure with asynchronous method (Swift 2.0)

时间:2016-02-12 19:28:36

标签: swift asynchronous mapkit completionhandler

I'm working on a project that calculates the ETA between various coordinates on a MapView. I use the asynchronous method calculateETAWithCompletionHandler to get the ETA between two coordinates and because calculateETAWithCompletionHandler is asynchronous, my code is not linear.

I need my code to be linear in order to display correct ETA information in a TableView, so I've tried to implement a closure to return in the asynchronous call. However, this still hasn't made my code linear. The following is what I have so far,

override func viewDidLoad() {
    var etaBetween1n2 = 0

    let point1 = MKPointAnnotaion()
    point1.coordinate = CLLocationCoordinate2D(latitude: 36.977317, longitude: -122.054255)
    point1.title = "Point 1"
    mapView.addAnnotation(point1)

    let point2 = MKPointAnnotaion()
    point2.coordinate = CLLocationCoordinate2D(latitude: 36.992781, longitude: -122.064729)
    point2.title = "Point 2"
    mapView.addAnnotation(point2)

    print("A")

    // Closure
    calculateETA(point1, destination: point2) { (eta: Int) -> Void in
        print("B")
        etaBetween1n2 = eta
    }

    print("C")

}

// Calculates ETA between source and destination
// Makes calculateETAWithCompletionHandler call which is asynchronous
func calculateETA(source: MKPointAnnotation, destination: MKPointAnnotation, result: (eta: Int) -> Void) {
    var eta = 0

    let request = MKDirectionsRequest()
    let sourceItem = MKMapItem(placemark: MKPlacemark(coordinate: source.coordinate, addressDictionary: nil))
    request.source = sourceItem
    request.transportType = .Automobile

    let destinationItem = MKMapItem(placemark: MKPlacemark(coordinate: destination.coordinate, addressDictionary: nil))
    request.destination = destinationItem

    request.destination = destinationItem
    request.requestsAlternateRoutes = false
    let directions = MKDirections(request: request)

    directions.calculateETAWithCompletionHandler { (etaResponse, error) -> Void in
        if let error = error {
            print("Error while requesting ETA : \(error.localizedDescription)")
        } else {
            eta = Int((etaResponse?.expectedTravelTime)!)
            result(eta: eta)
        }
    }

}

I was hoping that the closure would make my code linear by printing,

A
B
C

but instead it still prints,

A
C
B

Am I implementing the closure incorrectly or are closures the wrong approach for this?

2 个答案:

答案 0 :(得分:1)

So a closure is a callback meaning it continue to run the code the rest of code after the call to that function is made, and then once it is ready it will run the callback function.

So if you want to do something after it has finished then you would put it in callback so where you put print("B")

If you can calculating something your code should not be linear because it will freeze the main thread and the screen would become unresponsive until the action has finished.

What are you trying to do after?

答案 1 :(得分:1)

If you want "C" to happen after B, then you have to invoke it from within the closure:

<?php
    function underline($page) {
        return (basename(__FILE__) === $page) ? 'class="underline"' : '';
    }
       ?>

<div class="navigation">
    <a href="index.php" <?php echo underline('index.php') ?>>Home<a>
    <a href="about.php" <?php echo underline('about.php') ?>>About me</a>
    <a href="contact.php" <?php echo underline('contact.php') ?>>Contact me!</a>
</div>

If "C" is a step more complicated than a simple print statement, which I'm sure it is. Encapsulate it in a function, and call the function from within the closure.

print("A")
calculateETA(point1, destination: point2) { (eta: Int) -> Void in
    print("B")
    etaBetween1n2 = eta

    print("C")
}