0

I'm trying to get data from my database and after obtaining a piece of data, using that piece of data to find a new piece of data.

At the end, I can piece these together and return the derived data. I'm not sure this is the best way to approach this, but this is where im at as of now.

My problem is that each call to the database (Firebase) is async and therefore I need to somehow wait for the async to finish, before going on.

I've looked at dispatch group and heres what I have so far:

 let taskGroup = DispatchGroup()

    for buildingKey in building.allKeys
    {
      var aprt = NSDictionary()

      taskGroup.enter()
         // ASYNC REQUEST
      getAbodesWithUID(UID: buildingKey as! String, callback: { (success, abodes) in
        aprt = abodes
        taskGroup.leave()
      })

      taskGroup.enter()
      for key in aprt.allKeys
      {
        // ASYNC REQUEST
        getTenantsWithAprt(UID: key as! String, callback: { (success, user) in
          for userKey in user.allKeys
          {
            let dict = NSMutableDictionary()
            dict.setValue(((building[buildingKey] as? NSDictionary)?["Address"] as? NSDictionary)?.allKeys[0] as? String, forKey: "Building")
            dict.setValue((user[userKey] as? NSDictionary)?["Aprt"], forKey: "Number")
            dict.setValue((user[userKey] as? NSDictionary)?["Name"], forKey: "Name")
            dict.setValue(userKey, forKey: "UID")
            dict.setValue((user[userKey] as? NSDictionary)?["PhoneNumber"], forKey: "Phone")
            apartments.append(dict)
          }
          taskGroup.leave()
        })
      }
    }

    taskGroup.notify(queue: DispatchQueue.main, execute: {
      print("DONE")
      callback(true, apartments)
    })

I can't seem to get it to callback properly

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
huddie96
  • 1,240
  • 2
  • 13
  • 26
  • I would use completion handlers for this. – Jake Mar 19 '18 at 01:02
  • @Jake Can you explain a bit furthur? Isnt callback a completion handler? – huddie96 Mar 19 '18 at 01:03
  • Yes it is. If you're trying to get a value, then find a new result using the value you just got, I would try to write a function with a completion handler, and nest your function within it's own completion handler to get your data one tier at a time. function( completion { if true { function(completion etc., )} )} – Jake Mar 19 '18 at 01:06
  • @Jake But its all in a for loop. So where would i put the main functions completion handler? If its outside the nested part, the nested part is async so the main completion handler will be called before they finish and if its inside, it will get called once, the first time – huddie96 Mar 19 '18 at 01:08

2 Answers2

1

First, you should be iterating over aprt.allKeys inside of the callback for getAbodesWithUID, other wise, when the for loop executes aprt will be an empty dictionary.

Secondly, the taskGroup.enter() call above that for loop should be inside of the for loop, because it needs to be called once for every key. It should be placed where the // ASYNC REQUEST comment currently is.

Shadowfacts
  • 1,038
  • 10
  • 22
0

This is precisely what "promises" are for is for. They are available in Swift via a 3rd party add-in. A popular way to do this is to push all your reads/gets into an array. Then you promise.all(yourArray) which returns the array of results/values that you then iterate over to get at each one.

From this other answer:

You can look into when which may provide what you need and is covered here.

Use the loop to put your promises into an array and then do something like this:

when(fulfilled: promiseArray).then { results in
    // Do something
}.catch { error in
    // Handle error
}
Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91
  • Swift does NOT have promises. You have to use a Swift 3rd party SDK for them. Please edit your answer. – Smartcat Mar 19 '18 at 04:04