Kendel
Kendel

Reputation: 1708

Firebase Query Running At Wrong Time

For some reason when I run the following method, the first query runs after the method completes. I tried using a dispatch block in order to force the query to run first, but the query never runs at all then and the app simply freezes. Let me know if you know what is wrong.

Method without dispatch group:

func loadConversations() {
    let ref = FIRDatabase.database().reference()
    let convoRef = ref.child("users").child(FIRAuth.auth()!.currentUser!.uid).child("conversations")
    var conversationID = [String]()
    print(1)
    convoRef.queryOrderedByKey().observeSingleEvent(of: .value, with: { (snapshot) in
        let enumerator = snapshot.children
        print(2)
        while let rest = enumerator.nextObject() as? FIRDataSnapshot {
           print(3)
            if let id = rest.value as? String{
                conversationID.append(id)
                print(id)
            }
        }
    })
    print(4)
    print("size: \(conversationID.count)")
    for id in conversationID {
     print(5)
       ref.child("conversations").queryEqual(toValue: id).observeSingleEvent(of: .value, with: { (snapshot) in
      print(6)
            if let convo = snapshot.value as? [String : AnyObject] {
          print(7)
                let conversation = Conversation()
                conversation.conversationID = id
                conversation.name = "Temporary test name"
                self.Conversations.append(conversation)
            }
       })
        ref.removeAllObservers()
    }
    print(8)
    self.conversationTableView.reloadData()
    ref.removeAllObservers()

}

This prints:

1
4
size: 0
8
2
3
-KZyMMzXmkQC_OF0T08_

With the dispatch group:

func loadConversations() {
    let dispatchGroup = DispatchGroup() 
    let ref = FIRDatabase.database().reference()
    let convoRef = ref.child("users").child(FIRAuth.auth()!.currentUser!.uid).child("conversations")
    var conversationID = [String]()
    print(1)


    dispatchGroup.enter()
    convoRef.queryOrderedByKey().observeSingleEvent(of: .value, with: { (snapshot) in

        let enumerator = snapshot.children
        print(2)
        while let rest = enumerator.nextObject() as? FIRDataSnapshot {
           print(3)
            if let id = rest.value as? String{
                conversationID.append(id)
                print(id)
                dispatchGroup.leave()
            }
        }


    })

    print(4)
    dispatchGroup.wait()
    print("size: \(conversationID.count)")

    for id in conversationID {
     print(5)
       ref.child("conversations").queryEqual(toValue: id).observeSingleEvent(of: .value, with: { (snapshot) in
      print(6)
            if let convo = snapshot.value as? [String : AnyObject] {
          print(7)
                let conversation = Conversation()
                conversation.conversationID = id
                conversation.name = "Temporary test name"
                self.Conversations.append(conversation)
            }
       })
    }
    print(8)
    self.conversationTableView.reloadData()
    ref.removeAllObservers()
}

This prints

1
4

but then it just freezes and waits. The query never runs.

I am not sure why the query just does not appear to be entered. When the query is entered, it works perfectly fine, but it is entered too late. Any help is greatly appreciated. Thanks!

Upvotes: 1

Views: 265

Answers (1)

Rikh
Rikh

Reputation: 4232

This is simply because Firebase queries are executed on a background thread as it is essentially a network call. Hence the response comes after your method completes, otherwise the UI will be blocked until a response comes from Firebase

You need to write a closure inside your query response to execute a block of code as soon as you get the response.

func loadConversations(completion:@escaping (Array<String>) -> Void) -> Void {
    let ref = FIRDatabase.database().reference()
    let convoRef = ref.child("users").child(FIRAuth.auth()!.currentUser!.uid).child("conversations")
    var conversationID = [String]()
    print(1)
    convoRef.queryOrderedByKey().observeSingleEvent(of: .value, with: { (snapshot) in
        let enumerator = snapshot.children
        print(2)
        while let rest = enumerator.nextObject() as? FIRDataSnapshot {
           print(3)
            if let id = rest.value as? String{
                conversationID.append(id)
                print(id)
            }
        }
        completion(conversationID)
    })
}

This will send your call back to wherever it was called from and inside

loadConversations { (array) in

    //do something with this value and execute next query
}

Upvotes: 2

Related Questions