我如何保证 Swift 闭包(引用 Firebase)在我继续之前完全执行?

How can I guarantee that a Swift closure (referencing Firebase) fully executes before I move on?

我有多个带有闭包的查询快照,其中一些正在使用它之前的查询提供的数据。

我已经阅读了 GCD 并尝试使用 .enter() 和 .leave() 实现 DispatchGroup,但我显然做错了什么。

如果有人可以帮助我确切地说明如何强制一项任务在另一项任务之前执行,那将解决我的问题。

如果您看不出来,我对此还比较陌生,非常感谢您的帮助。

//MARK: Get all userActivities with distance(All Code)
static func getAllChallengesWithDistanceAllCode(activity:String, completion: @escaping ([Challenge]) -> Void) {
    let db = Firestore.firestore()
    let currUserID = Auth.auth().currentUser!.uid
    var currUserName:String?
    var distanceSetting:Int?
    var senderAverage:Double?
    var senderBestScore:Int?
    var senderMatchesPlayed:Double?
    var senderMatchesWon:Double?
    var senderWinPercentage:Double?
    var validUserActivities = [Challenge]()
    
    db.collection("users").document(currUserID).getDocument { (snapshot, error) in
        if error != nil || snapshot == nil {
            return
        }
        currUserName = snapshot?.data()!["userName"] as? String
        distanceSetting = snapshot?.data()!["distanceSetting"] as? Int
    }
    db.collection("userActivities").document(String(currUserID + activity)).getDocument { (snapshot, error) in
        //check for error
        //MARK: changed snapshot to shapshot!.data() below (possible debug tip)
        if error != nil {
            //is error or no data..??
            return
        }
        if snapshot!.data() == nil {
            return
        }
        //get profile from data proprty of snapshot
        if let uActivity = snapshot!.data() {
            senderBestScore = uActivity["bestScore"] as? Int
            senderMatchesPlayed = uActivity["played"] as? Double
            senderMatchesWon = uActivity["wins"] as? Double
            senderAverage = uActivity["averageScore"] as? Double
            senderWinPercentage = round((senderMatchesWon! / senderMatchesPlayed!) * 1000) / 10
        }
    }
    
    if distanceSetting != nil {
        db.collection("userActivities").whereField("activity", isEqualTo: activity).getDocuments { (snapshot, error) in
            if error != nil {
                print("something went wrong... \(String(describing: error?.localizedDescription))")
                return
            }
            if snapshot == nil || snapshot?.documents.count == 0 {
                print("something went wrong... \(String(describing: error?.localizedDescription))")
                return
            }
            if snapshot != nil && error == nil {
                let uActivitiesData = snapshot!.documents
                for uActivity in uActivitiesData {
                    let userID = uActivity["userID"] as! String
                    UserService.determineDistance(otherUserID: userID) { (determinedDistance) in
                        if determinedDistance! <= distanceSetting! && userID != currUserID {
                            var x = Challenge()
                            //Sender
                            x.senderUserID = currUserID
                            x.senderUserName = currUserName
                            x.senderAverage = senderAverage
                            x.senderBestScore = senderBestScore
                            x.senderMatchesPlayed = senderMatchesPlayed
                            x.senderMatchesWon = senderMatchesWon
                            x.senderWinPercentage = senderWinPercentage
                            //Receiver
                            x.receiverUserID = userID
                            x.receiverUserName = uActivity["userName"] as? String
                            x.receiverAverage = uActivity["averageScore"] as? Double
                            x.receiverBestScore = uActivity["bestScore"] as? Int
                            if (uActivity["played"] as! Double) < 1 || (uActivity["played"] as? Double) == nil {
                                x.receiverMatchesPlayed = 0
                                x.receiverMatchesWon = 0
                                x.receiverWinPercentage = 0
                            } else {
                                x.receiverMatchesPlayed = uActivity["played"] as? Double
                                x.receiverMatchesWon = uActivity["wins"] as? Double
                                x.receiverWinPercentage = ((uActivity["wins"] as! Double) / (uActivity["played"] as! Double) * 1000).rounded() / 10
                            }
                            if uActivity["playStyle"] as? String == nil {
                                x.receiverPlayStyle = "~No PlayStyle~"
                            } else {
                                x.receiverPlayStyle = uActivity["playStyle"] as! String
                            }
                            x.activity = activity
                            
                            //append to array
                            validUserActivities.append(x)
                        }
                    }
                }
                completion(validUserActivities)
            }
        }
    }
}

试试这个

static func getAllChallengesWithDistanceAllCode(activity:String, completion: @escaping ([Challenge]) -> Void) {
    let db = Firestore.firestore()
    let currUserID = Auth.auth().currentUser!.uid
    var currUserName:String?
    var distanceSetting:Int?
    var senderAverage:Double?
    var senderBestScore:Int?
    var senderMatchesPlayed:Double?
    var senderMatchesWon:Double?
    var senderWinPercentage:Double?
    var validUserActivities = [Challenge]()
    
db.collection("users").document(currUserID).getDocument { (snapshot, error) in
    if error != nil || snapshot == nil {
        return
    }
    currUserName = snapshot?.data()!["userName"] as? String
    distanceSetting = snapshot?.data()!["distanceSetting"] as? Int
    db.collection("userActivities").document(String(currUserID + activity)).getDocument { (snapshot, error) in
    //check for error
    //MARK: changed snapshot to shapshot!.data() below (possible debug tip)
    if error != nil {
        //is error or no data..??
        return
    }
    if snapshot!.data() == nil {
        return
    }
    //get profile from data proprty of snapshot
    if let uActivity = snapshot!.data() {
        senderBestScore = uActivity["bestScore"] as? Int
        senderMatchesPlayed = uActivity["played"] as? Double
        senderMatchesWon = uActivity["wins"] as? Double
        senderAverage = uActivity["averageScore"] as? Double
        senderWinPercentage = round((senderMatchesWon! / senderMatchesPlayed!) * 1000) / 10
        if snapshot != nil && error == nil {
            let uActivitiesData = snapshot!.documents
            for uActivity in uActivitiesData {
                let userID = uActivity["userID"] as! String
                UserService.determineDistance(otherUserID: userID) { (determinedDistance) in
                    if determinedDistance! <= distanceSetting! && userID != currUserID {
                        var x = Challenge()
                        //Sender
                        x.senderUserID = currUserID
                        x.senderUserName = currUserName
                        x.senderAverage = senderAverage
                        x.senderBestScore = senderBestScore
                        x.senderMatchesPlayed = senderMatchesPlayed
                        x.senderMatchesWon = senderMatchesWon
                        x.senderWinPercentage = senderWinPercentage
                        //Receiver
                        x.receiverUserID = userID
                        x.receiverUserName = uActivity["userName"] as? String
                        x.receiverAverage = uActivity["averageScore"] as? Double
                        x.receiverBestScore = uActivity["bestScore"] as? Int
                        if (uActivity["played"] as! Double) < 1 || (uActivity["played"] as? Double) == nil {
                            x.receiverMatchesPlayed = 0
                            x.receiverMatchesWon = 0
                            x.receiverWinPercentage = 0
                        } else {
                            x.receiverMatchesPlayed = uActivity["played"] as? Double
                            x.receiverMatchesWon = uActivity["wins"] as? Double
                            x.receiverWinPercentage = ((uActivity["wins"] as! Double) / (uActivity["played"] as! Double) * 1000).rounded() / 10
                        }
                        if uActivity["playStyle"] as? String == nil {
                            x.receiverPlayStyle = "~No PlayStyle~"
                        } else {
                            x.receiverPlayStyle = uActivity["playStyle"] as! String
                        }
                        x.activity = activity
                        
                        //append to array
                        validUserActivities.append(x)
                    }
                }
            }
            completion(validUserActivities)
        }
    }
}
    }
}
}

}

我通过执行以下操作解决了这个问题:

  1. 在for-in循环前加一个常量统计查询结果总数
    let documentCount = snapshot?.documents.count
  1. 在从 0 开始的 for-in 循环之前添加一个计数器
    var runCounter = 0
  1. 在 for-in 循环开始时每次迭代都会增加计数器
    runCounter += 1
  1. 将代码添加到 for-in 循环的末尾以调用完成处理程序以 return 结果
    if runCounter == documentCount {
                                                
        completion(validChallenges)
    }