Search code examples
swiftfirebaseasynchronousgoogle-cloud-firestoreclosures

Assigning the value inside of async closure Swift


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.


Solution

  • 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