Search code examples
swiftfirebasefirebase-realtime-databasecompletion

How to perform an action only after data are downloaded from Firebase


I'm trying to figure out how to trigger an action only after the observeSingleEvent in Firebase has finished its work.

The reason behind is that I need to do the following operations:

  • According to one input from the user I need to get some data from the database
  • When these data are completely downloaded, I need to process them a little bit and use to retrieve other data to be shown to the user

Currently I'm using this code:

    let listOfTags = [String]()
    var outputArray = [String]()

    if listOfTags.count != 0 {
        for tag in listOfTags {

            ref.child("tags").child(tag).observeSingleEvent(of: .value, with: { (snapshot) in

                if let temp = snapshot.children.allObjects as? [FIRDataSnapshot] {
                    for elem in temp {
                        outputArray.append(elem.key)
                    }
                }

                // ...
            }) { (error) in
                print(error.localizedDescription)
            }
        }

        // HERE I WANT TO DO AN ACTION "ONLY" AFTER THE FOR LOOP IS
        // COMPLETED AND THE "outputArray" IS FILLED WITH ALL VALUES
        doSomethingHere()

    }

But I noticed that the function doSomethingHere() is executed immediately when the outputArray is still EMPTY! So it does nothing.

Then my question is, how can I structure the program such that this function is executed after the downloading process is completed? Maybe observeSingleEvent is not the correct Firebase method to be used? For this specific case I don't want to observe any change (because this list is not changing so often), I just need to download data and do something else with these data later ..


Solution

  • Firebase is asynchronous so to make code execute in a defined order, you need to 'wait' for firebase to return data.

    The closures that go with Firebase functions do just that - the code within the closure executes when the data (snapshot) is valid.

    Move doSomethingHere() to within the Firebase closure and it will execute in the correct order - in this case

    if listOfTags.count != 0 {
        for tag in listOfTags {
    
            ref.child("tags").child(tag).observeSingleEvent(of: .value, with: { (snapshot) in
    
                if let temp = snapshot.children.allObjects as? [FIRDataSnapshot] {
                    for elem in temp {
                        outputArray.append(elem.key)
                    }
                    doSomethingHere() //array is populated at this point
                }
            })
        }
    }
    

    Note you can also remove this

    if listOfTags.count != 0
    

    as if listOfTags is 0, the for loop will not execute.