【问题标题】:Using Firestore DB, how can I break out of a for loop inside of a snapshot listener when a certain condition is met?使用 Firestore DB,当满足特定条件时,如何在快照侦听器内打破 for 循环?
【发布时间】:2020-04-08 00:03:15
【问题描述】:

一旦满足if myDay == self.daysOfWeek[self.picker.selectedRow(inComponent: 0)] 这个条件,我就会尝试跳出这个监听器,但是我没有得到我想要的结果。

一旦满足条件,它就会继续循环遍历所有文档。如何正确编码?

let addAction = UIAlertAction(title: "Add Workout", style: .default) { (UIAlertAction) in

    if self.dayCount != 0 {
        print("\(self.dayCount)")

        self.colRef.addSnapshotListener { (querySnapshot, err) in

            if let err = err
            {
                print("Error getting documents: \(err)");
            }
            else
            {
                for document in querySnapshot!.documents {
                    let myData = document.data()
                    let myDay = myData["dow"] as? String ?? ""

                    print(self.daysOfWeek[self.picker.selectedRow(inComponent: 0)])
                    print(myDay)
                    if myDay == self.daysOfWeek[self.picker.selectedRow(inComponent: 0)] {
                        containsDay = true
                        print(containsDay)
                        dayId = document.documentID
                        workoutColRef = Firestore.firestore().collection("/users/\(self.userIdRef)/Days/\(dayId)/Workouts/")

                        break //This Break needs to be somewhere else???
                    }
                }
            }
        }

        if containsDay == true {
            //Create new workout and store within the selectedDay.

            workoutColRef.addDocument(data: ["workout" : "\(self.textField2.text!)", "dayRef" : "\(dayId)"])

            self.loadDays()

        } else {
            //Create new day as well as a new workout, and store the workout within the day.

            let newDayRef = self.colRef.addDocument(data: ["dow" : "\(self.daysOfWeek[self.picker.selectedRow(inComponent: 0)])"])

            Firestore.firestore().collection("/users/\(self.userIdRef)/Days/\(newDayRef.documentID)/Workouts/").addDocument(data: ["workout" : "\(self.textField2.text!)", "dayRef" : newDayRef])

            newDayRef.getDocument { (docSnapshot, err) in
                if let err = err
                {
                    print("Error getting documents: \(err)");
                }
                else
                {
                    let myData = docSnapshot!.data()
                    let myDay = myData!["dow"] as? String ?? ""
                    self.daysArray.append(myDay)
                }
            }

            self.dayIdArray.append(newDayRef.documentID)

            self.loadDays()

        }
    } else {
        self.dayCount += 1 //If there are no days/workouts, we create new day as well as a new workout, and store the workout within the day.

        let newDayRef = self.colRef.addDocument(data: ["dow" : "\(self.daysOfWeek[self.picker.selectedRow(inComponent: 0)])"])

        Firestore.firestore().collection("/users/\(self.userIdRef)/Days/\(newDayRef.documentID)/Workouts/").addDocument(data: ["workout" : "\(self.textField2.text!)", "dayRef" : newDayRef])


        newDayRef.getDocument { (docSnapshot, err) in
            if let err = err
            {
                print("Error getting documents: \(err)");
            }
            else
            {
                let myData = docSnapshot!.data()
                let myDay = myData!["dow"] as? String ?? ""
                self.daysArray.append(myDay)
            }
        }

        self.dayIdArray.append(newDayRef.documentID)

        self.loadDays()   
    }
}

【问题讨论】:

  • 我在下面写了一个答案,但随后查找了一些 Swift 信息,据我所知,您的 break 应该与 for in 不同。您如何确定它会继续处理?
  • @FrankvanPuffelen 我更新了代码以显示整个操作。我确定它继续处理,因为它打印“真”,当我只调用一次操作时两次。
  • 您可能希望在该语句上放置一个断点并在调试器中单步执行代码。据我所知(但我绝对不是 Swift 专家),break 应该中止循环。但是,如果是这种情况,单步执行会很快告诉你。如果不是,我的回答可能仍然提供一种解决方法,即使我认为它应该不需要。
  • 这里有几个问题,但最重要的是这行if containsDay == true 将在self.colRef.addSnapshotListener 之后立即执行,这似乎不是您想要的。 Firebase 调用是异步的,闭包之后的代码将在 in 闭包的代码之前执行。

标签: ios swift firebase google-cloud-firestore


【解决方案1】:

问题中代码的第一个问题是Firestore是异步的。

闭包之后的任何代码都将在闭包中的代码之前执行。从服务器检索数据需要时间,并且闭包中的代码在检索到数据后运行。

所以这一行

if containsDay == true {

需要移动。

弗兰克的回答很好,但另一种解决方案是使用转义和休息

假设我们要检索用户的 uid - 在这种情况下,我们遍历用户节点以查找 Bob。

users
   uid_0
      name: "Frank"
   uid_1
      name: "Bill"
   uid_2
      name: "Bob"

这是我们调用函数的代码

self.lookForBobsUid(completion: { bobsUid in
    print("Bob's uid is: \(bobsUid)")
})

然后是读取所有用户的函数,对其进行迭代,然后当我们找到 Bob 时,返回 Bobs uid 并跳出循环。

func lookForBobsUid(completion: @escaping ( (String) -> Void ) ) {
    let usersCollection = self.db.collection("users")
    usersCollection.getDocuments(completion: { snapshot, error in
        if let err = error {
            print(err.localizedDescription)
            return
        }

        guard let documents = snapshot?.documents else { return }

        for doc in documents {
            let uid = doc.documentID
            let name = doc.get("name") as? String ?? "No Name"
            print("checking name: \(name)")
            if name == "Bob" {
                print("  found Bob, leaving loop")
                completion(uid)
                break
            }
        }
    })

    print("note this line will print before any users are iterated over")
}

请注意,我在代码末尾添加了一行来演示异步调用的性质。

综上所述,一般来说,通常可以避免遍历集合来查找内容。

看来您正在寻找解决方法

self.daysOfWeek[self.picker.selectedRow(inComponent: 0)]

如果是这样,建议查询 self.colRef 以获取该项目,而不是通过迭代来找到它;这将更快,使用更少的资源,并且还具有可扩展性 - 如果有 100,000 个节点可以迭代!

【讨论】:

    【解决方案2】:

    一个简单的方法是保留一个标志变量来指示您是否找到了文档:

    let foundIt = false
    for document in querySnapshot!.documents {
        if !foundIt {
            let myData = document.data()
            let myDay = myData["dow"] as? String ?? ""
    
            if myDay == self.daysOfWeek[self.picker.selectedRow(inComponent: 0)] {
                containsDay = true
                dayId = document.documentID
                workoutColRef = Firestore.firestore().collection("/users/\(self.userIdRef)/Days/\(dayId)/Workouts/")
    
                foundIt = true
            }
        }
    }
    

    【讨论】:

    • 我相信 OP 的代码中存在异步问题。此外,即使在 foundIt = true 之后,for 循环也会继续运行,因此在该行之后添加一个 break。
    猜你喜欢
    • 1970-01-01
    • 2010-11-06
    • 2021-09-21
    • 2020-01-08
    • 2020-04-21
    • 1970-01-01
    • 2020-06-16
    • 1970-01-01
    • 2016-07-24
    相关资源
    最近更新 更多