Search code examples
swiftasynchronousasync-awaitdispatchdispatch-queue

how to use a construct await sync inside a closure


I have a code that loads data from firebase, the data is not loaded immediately and I want to load the data first, and then pass the data to the delegate. To do this, I used the wait construct and the synchronous loadData function

let dateUser = await loadData(phoneNumber: phoneNumber)

According to the idea, the dateUser variable should wait until the function returns the result, but the function returns the result immediately without waiting for the data to load

Can you tell me what the error is, I think that you need to wait inside the loadData function, but where?

Everywhere there are examples of how to use this construction for loading JSON files, but I did not find where it would be used inside the closure

here is a sample code

struct LoadData {
    
    let db = Firestore.firestore()
    var delegate: LoadDataDelegate?    
    
    func getData(phoneNumber:String){
        Task.init {
            let dateUser = await loadData(phoneNumber: phoneNumber)
            print("Делегат получил дату - ", dateUser)
            delegate!.loadData(date: dateUser)
        }
    }
    
    func loadData(phoneNumber: String) async -> TimeInterval? {
        
        var existingUser = false /// Проверка существует ли пользователь с данным номером телефона в базе
        var date: TimeInterval?
        
        db.collection("Users").getDocuments { QuerySnapshot, Error in
            if let err = Error {
                print("Ошибка получения данных - \(err)")
            }
            
            for document in QuerySnapshot!.documents {
                
                if document.documentID == phoneNumber { /// Если текущий пользователь уже был зарегестрирован
                    
                    existingUser = true
                    date =  document["dateFirstLaunch"] as? TimeInterval /// Преобразуем данные из FireBase
                    print("это дата ",date!)
                    
                }
            }
            
            if existingUser == false { /// Если такого пользователя не было
                
                print("New User")
                db.collection("Users").document(phoneNumber).setData(["dateFirstLaunch" : NSDate().timeIntervalSince1970]) { error in
                    if let  err = error {
                        print("Ошибка записи данных в обалко - \(err)")
                    }
                }
            }
        }
        
        return date
        
    }
    
}

protocol LoadDataDelegate {
    func loadData(date: TimeInterval?)
    
}

I tried to wait inside the loadData function, but as a result, either errors come out or nothing works


Solution

  • You are not awaiting any of the asynchronous operations in loadData, so loadData returns immediately.

    You should use the async versions of the Firebase Firestore APIs , rather than those with completion handlers.

    Instead of passing a completion handler, try await the asynchronous operations. Simply put the code in the completion handler after the try await line, and handle the error with a catch block.

    For example:

    func loadData(phoneNumber: String) async -> TimeInterval? {
        do {
            let querySnapshot = try await db.collection("Users").getDocuments()
            for document in querySnapshot.documents {
                if document.documentID == phoneNumber {
                    return document["dateFirstLaunch"] as? TimeInterval
                }
            }
        } catch {
            print("Ошибка получения данных - \(error)")
            // I assume you want to return nil immediately in this case and not write to the document
            return nil
        }
        print("New User")
        do {
            try await db.collection("Users").document(phoneNumber).setData(["dateFirstLaunch" : Date().timeIntervalSince1970])
        } catch {
            print("Ошибка записи данных в обалко - \(error)")
        }
        // I assume that in the new user case, you want to return nil
        return nil
    }