0

I am building arrays via a for in loop inside a closure, then passing these arrays through a segue. I am able to print the contents of the arrays from within the closure, but when I access the array from outside, it is always empty.

I have tried to use .reloadData() to update the arrays outside of the closure, but I can't seem to get it quite right. Does anyone know if this is the correct approach?

Here is my code:

var distances: [Double]
var journeyTimes: [Double]

        directions.calculate { response, error in
            if let route = response?.routes.first {
               print("Distance: \(route.distance/1000) km, ETA: \(route.expectedTravelTime/60) mins")

            self.distances.append(route.distance/1000)
            self.journeyTimes.append(route.expectedTravelTime/60)

                print(self.distances)
                print(self.journeyTimes)

            } else {
                print("Error!")
            } updateArrays()
        }

func updateArrays() {

    self.collectionView.reloadData()
}

The print statements above produce results, and my prepare for segue carries distances and journeyTimes through, but they are always empty when I try to access them.

I think I need to update the distances and journeyTimes arrays before prepare for segue, outside of the closure, but I don't know how to do it.

Grateful for any help on this

Thanks

  • Search SO for completion handlers or callbacks or for starter you can check this: https://thatthinginswift.com/completion-handlers/ or check this: https://stackoverflow.com/questions/30401439/how-could-i-create-a-function-with-a-completion-handler-in-swift – 3stud1ant3 Oct 08 '17 at 12:48

1 Answers1

0

One way to address this is by using a Dispatch Group. Create a new constant:

let myDispatchGroup = DispatchGroup()
var distances: [Double]
var journeyTimes: [Double]

Just before you make your network call add:

myDispatchGroup.enter()

Make sure you add it before the asynch network call begins.

After your arrays have been fully updated and your are about to exit your Asynchronous network call add:

self.myDispatchGroup.leave()

Only after the number of enters and leaves on the myDispatchGroup are equal will this function be called. Add it just before you perform your segue

func yourFunctionThatCallsSegue() {

   myDispatchGroup.notify(queue: DispatchQueue.main) {
      let destinationViewController = segue.destination as! YourDestinationViewController
      destinationViewController.distances = self.distances // you are in a dispatch group closure
      destinationViewController.journeyTimes = self.journeyTimes 
      performSegue(withIdentifier: "Your Identifier", sender: AnyClass.self)        
     }
}
Martin Muldoon
  • 3,388
  • 4
  • 24
  • 55
  • Thanks Martin. My distance/time calculation sits in a for in loop, so would you put the dispatchGroup enter/leave before and after the for in loop (to ensure the arrays are fully populated), or inside it? My segue is performed at the same time as directions.calculate (in my code above), so I am using func prepare(forSegue....) to send arrays over to my destination VC. Can I still use your segue approach in here? – mathleticolewis Oct 08 '17 at 16:33
  • Your network call is asynchronous. The app has no idea how long it will take. So your program continues while the network call runs in the background. This is why when you print the array outside the network call nothing is there. It's still fetching. You must place the myDispatch.enter() just before making the call, and myDispatchleave() after the loops that fill the array. – Martin Muldoon Oct 08 '17 at 16:41
  • To answer your second question, In this case I would do everything in the function that calls your segue. Not in prepare. – Martin Muldoon Oct 08 '17 at 16:43