0

I'm trying to get the count of documents where located in Firebase. Whenever I try to assign the count of documents a variable in closure, the value is null. According to articles, networking takes some time and it happens asynchronously. So due to asynchronous behaviour, returning a value inside a closure might happen before assignment of the value.

I tried to add dispatchqueue.main.async but it didn't work.

Here is my code

 func getEventCount () -> Int? {
    var count: Int?
    db.collection("Events").whereField("owner", isEqualTo: currentUser.email).getDocuments { (snapshot, error) in
        if error != nil {
            print(error)
        }else {
            DispatchQueue.main.async {
                
                if let snapshot = snapshot {
                    count = snapshot.count
                }
                
            }
        }
    }
    return count
}

My main goal is to get count of documents from database and assign a variable called count.

Rutkay Karabulak
  • 107
  • 1
  • 2
  • 12
  • You don't need this `DispatchQueue.main.async` inside the Firebase closure and you can't use `return` there as Firebase is asynchronous. Also, what's the plan for that `count` variable. If we had a bit more info we may be able to suggest a solution. – Jay May 12 '20 at 18:14
  • @Jay i did edit the question can you check? – Rutkay Karabulak May 12 '20 at 20:12
  • So others closed the question and the provided link is a good read. When populating a dataSource, the dataSource should be class var array, and then use the data from within the Firebase closure to populate it. That array determines the count used by numberOfRowsInSection. See my answer to [this question](https://stackoverflow.com/questions/43823808/access-firebase-variable-outside-closure/43832208#43832208). My answer to [this question](https://stackoverflow.com/questions/61238857/firebase-getdocument-querysnapshot-is-not-working/61276604#61276604) is a simple completion handler example. – Jay May 12 '20 at 20:39

1 Answers1

1

Once it is a Async call - you cannot synchronously return the value from the function. You should accept a callback to the function that will accept the count. That callback function or closure will be passed the value asynchronously.

func getEventCount (callback: @escaping(Result<Int, Error>) -> Void) {
    db.collection("Events").whereField("owner", isEqualTo: currentUser.email).getDocuments { (snapshot, error) in
        if error != nil {
            let result = Result.failure(error)
            callback(result)
        }else if let snapshot = snapshot {
               let result = Result.success(snapshot.count)
               callback(result)
        } else {
            let result = Result.failure(SomeCustomAppError)
            callback(result)
        }
    }
}

Then you can call this function passing in a callback

self.getCount() { result in
  switch result {
   case .success(let count): /// use count
   print(count)
   // only here u can assign the count value to ur variable
   case .error(let error): /// handle error
   print(error.localizedDescription)
  }
}

Note: In the above I've used the Result datatype from Swift standard library - https://developer.apple.com/documentation/swift/result so that both error or result can be passed back

ram
  • 78
  • 1
  • 8