1

I have some code that creates MapKit directions using the

directions.calculateDirectionsWithCompletionHandler({(response, error) in

method.

The code works fine -- within the completion handler, but I can't figure out how to access the data outside the completion handler. I have defined the vars used outside the completion handler.

I realize that the completion handler runs asynchronously, but I don't know what to do to compensate for that.

Finally, the code presented is in a Swift Playground because the completion handler closure runs there -- in an app, the completion handler closure never runs because of an SSL error.

Here's the code as minimal as I could make it:

//: Playground - noun: a place where people can play

import UIKit
import MapKit
import CoreLocation
import Contacts
import XCPlayground

XCPSetExecutionShouldContinueIndefinitely()

var directionsArray = [String]()
var addressString = ""

// Identify the Source and Destination
let source = MKMapItem(placemark: MKPlacemark(
    coordinate: CLLocationCoordinate2DMake(32.2345760,-110.8444420), addressDictionary: nil))
let destination = MKMapItem(placemark: MKPlacemark(
    coordinate: CLLocationCoordinate2DMake(34.104908,-118.137903), addressDictionary: nil))
let theLocation = CLLocation(latitude: source.placemark.coordinate.latitude, longitude: source.placemark.coordinate.longitude)

let directionsRequest = MKDirectionsRequest()
directionsRequest.source = source
directionsRequest.destination = destination
directionsRequest.requestsAlternateRoutes = true

let directions = MKDirections(request: directionsRequest)

print("before: directions.calculateDirectionsWithCompletionHandler addressString: \(addressString)")
print("before: directions.calculateDirectionsWithCompletionHandler directionsArray: \(directionsArray)")
directions.calculateDirectionsWithCompletionHandler({(response, error) in
    var selectedRoute = 1

    addressString += "\n source: 6922 E 1st St, 85710 Tucson, AZ United States"
    addressString += "\n Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States"

    print("\nwithin: directions.calculateDirectionsWithCompletionHandler: \n")
    print("error: \(error)")
    print("response!.routes.count \(response!.routes.count)")
    print("response!.routes[selectedRoute] \(response!.routes[selectedRoute])")

    let theSteps = response?.routes[selectedRoute].steps.count as Int!

    print("theSteps: \(theSteps)")
    print("distance: \(response!.routes[selectedRoute].distance) meters") // time \(response?.routes.first?.time)")
    addressString += "\n distance: \(response!.routes[selectedRoute].distance) meters"
    let myRoute = response?.routes[selectedRoute]
    print("myRoute!.polyline.pointCount \(myRoute!.polyline.pointCount)")
    print("myRoute!.steps[0].polyline.points(): \(myRoute!.steps[0].polyline.points())")
    print("polyline.points \(myRoute!.polyline.points())")

    // Build an array of Route Step Coordinates for later inclusion in Directions
    var stepCoordinates = [CLLocationCoordinate2D]()
    for step in myRoute!.steps as [MKRouteStep] {
        let pointCount = step.polyline.pointCount
        var cArray = UnsafeMutablePointer<CLLocationCoordinate2D>.alloc(pointCount)
        step.polyline.getCoordinates(cArray, range: NSMakeRange(0, pointCount))

        for var c=0; c < pointCount; c++ {
            let coord = cArray[c]
            if c == 0 {
               var theCoordinate = CLLocationCoordinate2DMake(coord.latitude, coord.longitude)
            stepCoordinates.append(theCoordinate)
            }
        }
        cArray.dealloc(pointCount)
    }

    // get the Directions Steps including the latitidude / longitude from thearray of Step Coordinates
    for index in  0..<theSteps {
        let lat = stepCoordinates[index].latitude
        let lon = stepCoordinates[index].longitude
        directionsArray.append("\n steps[\(index)] \(lat),\(lon) - \(myRoute!.steps[index].distance) meters - \(myRoute!.steps[index].instructions)")
    }

    print("\nwithin: directions.calculateDirectionsWithCompletionHandler: addressString: \(addressString)")
    print("\nwithin: directions.calculateDirectionsWithCompletionHandler: directionsArray: \(directionsArray)")
})

print("\nafter: directions.calculateDirectionsWithCompletionHandler: addressString: \(addressString)")
print("after: directions.calculateDirectionsWithCompletionHandler: directionsArray: \(directionsArray)\n")

Here's the Console Output:

before: directions.calculateDirectionsWithCompletionHandler addressString:

before: directions.calculateDirectionsWithCompletionHandler directionsArray: []

after: directions.calculateDirectionsWithCompletionHandler: addressString: 
after: directions.calculateDirectionsWithCompletionHandler: directionsArray: []

2015-07-06 14:00:58.421 GetDirectionsBasic[9758:4735303] Failed to obtain sandbox extension for path=/var/folders/7l/cgpb6wr9489b1qhxl8y38hvm0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-98692A18-CEBC-4354-8E6A-5B12BA647B9E/Library/Caches/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-98692A18-CEBC-4354-8E6A-5B12BA647B9E. Errno:1
2015-07-06 14:00:58.422 GetDirectionsBasic[9758:4735303] Failed to obtain sandbox extension for path=/var/folders/7l/cgpb6wr9489b1qhxl8y38hvm0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-98692A18-CEBC-4354-8E6A-5B12BA647B9E/Library/Caches/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-98692A18-CEBC-4354-8E6A-5B12BA647B9E. Errno:1

within: directions.calculateDirectionsWithCompletionHandler: 

error: nil
response!.routes.count 3
response!.routes[selectedRoute] <MKRoute: 0x7fd7e0549220>
theSteps: 15
distance: 821009.0 meters
myRoute!.polyline.pointCount 2743
myRoute!.steps[0].polyline.points(): 0x00007fd7e1073000
polyline.points 0x00007fd7e1073000

within: directions.calculateDirectionsWithCompletionHandler: addressString: 
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters

within: directions.calculateDirectionsWithCompletionHandler: directionsArray: [
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St, 
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave, 
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd, 
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix, 
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W, 
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego, 
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio, 
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86, 
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W, 
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left, 
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd, 
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd, 
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave, 
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr, 
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left]
user2984842
  • 71
  • 1
  • 6
  • the completion block is called after an asynchronous function so the code after the block is actually executed before the block (asynchronous functions take time). Thus, addressString and directionsArray aren't updated. – ad121 Jul 06 '15 at 22:19
  • @ad121 I understand that ... But I can't be the first, or only, one who wants to save the data created within the asynchronous completion block, for later use. I even found a proposed solution involving CGD to fire the main thread when the asynchronous completion block thread has finished -- that didn't work either. It amazes me that with all the iOS activity with MapKit -- that no [working] sample code is available -- for directions, geocoding ... and other things using asynchronous completion blocks. Frustrated -- all these neat new map features at WWDC 2015 -- and no way to use them. – user2984842 Jul 07 '15 at 04:49
  • I'd use a dispatch group together with a dispatch notify to collect the updated values after all operations. Basically dg.enter(), dg.leave() -when done and dg.notify{} after all executions leave the notify completion handler is called. – Tommie C. Dec 09 '18 at 15:24

3 Answers3

3

You're right that there's a fundamental issue about asynchronicity here. And it really has nothing to do with MapKit or most of the other details of your code, so I've abstracted them away in the answer that follows.

"Asynchronous" means that, for anything that looks like this:

code with colored annotation

...the green and blue blocks run before the brown block:

execution order of colors

(Also, the time in between the blue and brown blocks could be arbitrarily short or long, with the main thread updating the UI during that time.)

This means:

  • any code in the blue block is unaware of things that happen in the brown block, because it hasn't happened yet. (If otherwise happens, you've violated causality... notify Stephen Hawking. And file a Radar.)

  • anything you want to have happen as a result of code in the brown block needs to be triggered from within the brown block. (Or from somewhere else that's guaranteed to be "later".)

This also means that, if you're trying for a pattern like:

func getDirections() -> String {
    var directions: [String]
    getMKDirectionsWithCompletionHandler() { result, error in 
        directions = // something from result
    }
    return directions
}

...you have a fundamentally unworkable design. You'll need to instead look at the places you want to call your getDirections() function and use its result, and design them to be asynchronous.

For example, if you wanted to do something like this:

let theDirections = getDirections()
destinationLabel.text = theDirections.last!

You'll instead need to do something like this:

getMKDirectionsWithCompletionHandler() { result, error in 
    let directions: [String] = // something from result
    destinationLabel.text = directions.last! // use it *in* the completion block
}

Or, remember how I said "somewhere else that's guaranteed to be later"? Suppose you want to display these strings in a table view:

class MyViewController: UITableViewController {
    var directions = [String]()

    func viewWillAppear() {
        getMKDirectionsWithCompletionHandler() { result, error in 
            directions = // something from result

            // make sure the UI updates after we have our result
            self.tableView.reloadData() 
        }
    }

    func tableView(tv: UITableView, numberOfRowsInSection section: Int) -> Int {
        return directions.count
    }

    func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let directionsItem = directions[indexPath.row]
        return // cell constructed from directionsItem
    }
}
rickster
  • 118,448
  • 25
  • 255
  • 308
  • Thanks for your response! I've been researching this for weeks, with no Success. I tried both your approaches with no success ... It would really help if you could post a working code snippet (hopefully a Playground). Thanks in advance. – user2984842 Jul 06 '15 at 23:45
1

With the help of a delay routine from @matt and a file read/write routine from @gooroo7, here is a working example in a Playground ...

I haven't been able to get it to work within an app because of SSL handshaking problems when initiating the get directions request:

mapDirections.calculateDirectionsWithCompletionHandler({(response, error) in

I've included the rather lengthy code and console output. The routines from @matt and @gooroo7 are documented where used. Also, the console output is rather long -- showing the before, during and after (the closure) status of the file and vars used.

Here's the Playground code:

//: Playground - noun: a place where people can play

import UIKit
import MapKit
import CoreLocation
import Contacts
import XCPlayground

XCPSetExecutionShouldContinueIndefinitely()

var directionsArray = [String]()
var addressString = String()

func setAddressString(theString:String){
    addressString = theString
    print("setAddressString \(addressString)")
}


// http://stackoverflow.com/questions/24097826/read-and-write-data-from-text-file
// answer by goroo7
//To avoid confusion and add ease, I have created two functions for reading and writing strings to files in the documents directory. Here are the functions:
//--------------------------------------------------------------------------------------------------------------
func writeToDocumentsFile(fileName:String,value:String) {
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] // as! NSString
    let path = documentsPath.stringByAppendingPathComponent(fileName)
    //    var error:NSError?
    do {
        try value.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding) //, error: nil)
    }
    catch let error as NSError {
        // Catch fires here, with an NSErrror being thrown from the value.writeToFile method
        print("A write to file error occurred, here are the details:\n \(error)")
    }
}

func readFromDocumentsFile(fileName:String) -> String {
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] //as! NSString
    let path = documentsPath.stringByAppendingPathComponent(fileName)
    let checkValidation = NSFileManager.defaultManager()
    //    var error:NSError?
    //    var file:String

    var file = ""
    if checkValidation.fileExistsAtPath(path) {
        do {
            try file = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding) as String
        }
        catch let error as NSError {
            // Catch fires here, with an NSErrror being thrown from the JSONObjectWithData method
            print("A read from file error occurred, here are the details:\n \(error)")
        }

    } else {
        file = "*ERROR* \(fileName) does not exist."
    }
    print("return file: \(file)")
    return file
}
//--------------------------------------------------------------------------------------------------------------


func getDirections() {
    print("getDirections")
    // Identify the Source and Destination
    let source = MKMapItem(placemark: MKPlacemark(
        coordinate: CLLocationCoordinate2DMake(32.2345760,-110.8444420), addressDictionary: nil))
    let destination = MKMapItem(placemark: MKPlacemark(
        coordinate: CLLocationCoordinate2DMake(34.104908,-118.137903), addressDictionary: nil))
    let theLocation = CLLocation(latitude: source.placemark.coordinate.latitude, longitude: source.placemark.coordinate.longitude)

    let directionsRequest = MKDirectionsRequest()
    directionsRequest.source = source
    directionsRequest.destination = destination
    directionsRequest.requestsAlternateRoutes = true

    let mapDirections = MKDirections(request: directionsRequest)

    print("before: directions.calculateDirectionsWithCompletionHandler addressString: \(addressString)")
    print("before: directions.calculateDirectionsWithCompletionHandler directionsArray: \(directionsArray)\n")
    mapDirections.calculateDirectionsWithCompletionHandler({(response, error) in
        let selectedRoute = 1

        let todaysDate:NSDate = NSDate()
        let dateFormatter:NSDateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "MM-dd-yyyy HH:mm"
        let DateInFormat:String = dateFormatter.stringFromDate(todaysDate)
        //print("\nDateInFormat: \(DateInFormat)\n")

        addressString = "\n..........addressString" //\n \(DateInFormat)"
        addressString += ("\n Date \(DateInFormat)")
        addressString += ("\n source: 6922 E 1st St, 85710 Tucson, AZ United States")
        addressString += ("\n Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States")

        print("\nwithin: directions.calculateDirectionsWithCompletionHandler: \n")
        print("error: \(error)")
        print("response!.routes.count \(response!.routes.count)")
        print("response!.routes[selectedRoute] \(response!.routes[selectedRoute])")

        let theSteps = response?.routes[selectedRoute].steps.count as Int!

        print("theSteps: \(theSteps)")
        print("distance: \(response!.routes[selectedRoute].distance) meters") // time \(response?.routes.first?.time)")
        addressString += ("\n distance: \(response!.routes[selectedRoute].distance) meters")
        let myRoute = response?.routes[selectedRoute]
        print("myRoute!.polyline.pointCount \(myRoute!.polyline.pointCount)")
        print("myRoute!.steps[0].polyline.points(): \(myRoute!.steps[0].polyline.points())")
        print("polyline.points \(myRoute!.polyline.points())")

        // Build an array of Route Step Coordinates for later inclusion in Directions
        var stepCoordinates = [CLLocationCoordinate2D]()
        for step in myRoute!.steps as [MKRouteStep] {
            let pointCount = step.polyline.pointCount
            var cArray = UnsafeMutablePointer<CLLocationCoordinate2D>.alloc(pointCount)
            step.polyline.getCoordinates(cArray, range: NSMakeRange(0, pointCount))

            for var c=0; c < pointCount; c++ {
                let coord = cArray[c]
                if c == 0 {
                   var theCoordinate = CLLocationCoordinate2DMake(coord.latitude, coord.longitude)
                stepCoordinates.append(theCoordinate)
                }
            }
            cArray.dealloc(pointCount)
        }

        // get the Directions Steps including the latitidude / longitude from thearray of Step Coordinates
        for index in  0..<theSteps {
            let lat = stepCoordinates[index].latitude
            let lon = stepCoordinates[index].longitude
            directionsArray.append("\n steps[\(index)] \(lat),\(lon) - \(myRoute!.steps[index].distance) meters - \(myRoute!.steps[index].instructions)")
            addressString += ("\n steps[\(index)] \(lat),\(lon) - \(myRoute!.steps[index].distance) meters - \(myRoute!.steps[index].instructions)")

        }

        //        let directions: [String] = addressString
        setAddressString(addressString)
        print("\nwithin: directions.calculateDirectionsWithCompletionHandler: addressString: \(addressString)")
        print("\nwithin: directions.calculateDirectionsWithCompletionHandler: directionsArray: \(directionsArray)")


        // http://stackoverflow.com/questions/24097826/read-and-write-data-from-text-file
        // answer by goroo7
        //To avoid confusion and add ease, I have created two functions for reading and writing strings to files in the documents directory. Here are the functions: ... Here is an example of their use:
        //--------------------------------------------------------------------------------------------------------------
        writeToDocumentsFile("addressString.txt",value: addressString)
        //--------------------------------------------------------------------------------------------------------------

    })
}
getDirections()

// http://stackoverflow.com/questions/24034544/dispatch-after-gcd-in-swift/24318861#24318861
// answer by @matt
// I use dispatch_after so often that I wrote a top-level utility function to make the syntax simpler:
//--------------------------------------------------------------------------------------------------------------
func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

//And now you can talk like this:

delay(3.0) {
    // do stuff
    let value = readFromDocumentsFile("addressString.txt")
    print("\nAfter Delay - readFromDocumentsFile written in closure: \(value)\n")  //Would output 'Hello world!'
    print("\nAfter Delay - read from external var addressString updated in closure: \(addressString)")
    print("\nAfter Delay - read from external var directionsArray updated in closure:\(directionsArray)")
}
//--------------------------------------------------------------------------------------------------------------

Here's the console output:

getDirections
before: directions.calculateDirectionsWithCompletionHandler addressString: 
before: directions.calculateDirectionsWithCompletionHandler directionsArray: []

2015-07-07 16:12:31.116 GetDirectionsBasic[1768:630471] Failed to obtain sandbox extension for path=/var/folders/7l/cgpb6wr9489b1qhxl8y38hvm0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-9B1F8F3C-4BBC-430F-AEFE-469C81A24CBE/Library/Caches/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-9B1F8F3C-4BBC-430F-AEFE-469C81A24CBE. Errno:1
2015-07-07 16:12:31.116 GetDirectionsBasic[1768:630471] Failed to obtain sandbox extension for path=/var/folders/7l/cgpb6wr9489b1qhxl8y38hvm0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-9B1F8F3C-4BBC-430F-AEFE-469C81A24CBE/Library/Caches/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-9B1F8F3C-4BBC-430F-AEFE-469C81A24CBE. Errno:1

within: directions.calculateDirectionsWithCompletionHandler: 

error: nil
response!.routes.count 3
response!.routes[selectedRoute] <MKRoute: 0x7fed8a212e60>
theSteps: 15
distance: 821009.0 meters
myRoute!.polyline.pointCount 2743
myRoute!.steps[0].polyline.points(): 0x00007fed89052a00
polyline.points 0x00007fed89052a00
setAddressString 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left

within: directions.calculateDirectionsWithCompletionHandler: addressString: 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left

within: directions.calculateDirectionsWithCompletionHandler: directionsArray: [
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St, 
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave, 
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd, 
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix, 
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W, 
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego, 
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio, 
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86, 
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W, 
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left, 
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd, 
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd, 
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave, 
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr, 
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left]
return file: 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left

After Delay - readFromDocumentsFile written in closure: 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left


After Delay - read from external var addressString updated in closure: 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left

After Delay - read from external var directionsArray updated in closure:[
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St, 
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave, 
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd, 
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix, 
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W, 
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego, 
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio, 
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86, 
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W, 
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left, 
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd, 
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd, 
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave, 
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr, 
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left]

Thanks to all who helped -- hope this is of some use to others!

user2984842
  • 71
  • 1
  • 6
  • A dispatch group would ensure that all the steps you want, finish before proceeding. Then you can wrap your UI into the main thread to update UI elements when all done. This makes the delay calls unnecessary. Goodness knows when those artifical delays will start causing you mysterious errors. (see the docs for more) – Tommie C. Sep 07 '18 at 01:53
0

Since completion handler runs on another thread, dispatch_async to the thread you want to use the data right inside of completion handler. This way you will access data after it actually arrives.

YOYOYO
  • 1
  • 1
  • That's not quite my problem. I want to use data created within the completion handler -- outside of the completion handler. For example, I want to create an external .gpx file to be used with the Apple Maps app. – user2984842 Jul 06 '15 at 23:53