我不了解完成处理程序,我的代码中需要一个

时间:2019-12-03 14:21:26

标签: swift xcode firebase uitableview google-cloud-firestore

我从Firestore捕获数据并将其分配给数组,另一个问题的人告诉我也许我需要一个完成处理程序,但是我不知道如何工作,如果您能帮助我编写代码并进行解释,那将非常有帮助这个概念对我来说。

class PetsTVC: UITableViewController {

    var db: Firestore!
    let uid = Auth.auth().currentUser?.uid
    var petslist = [String]()
    var pid = ""
    var pets = [paw]()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.loadPets()
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }

    func loadPets(){
        let photo1 = UIImage(named: "Log")
        db = Firestore.firestore()

        db.collection("users").document(uid!).getDocument { documentSnapshot, error in
            guard let document = documentSnapshot else {
                return
            }
            self.petslist = document["petslist"] as? Array ?? [""]
        }
        for pet in self.petslist {
            self.db.collection("pets").document(pet).getDocument { documentSnapshot, error in
                guard let document = documentSnapshot else {
                    return
                }
                guard let data = document.data() else {
                    return
                }
                let pid = data["pid"] as! String? ?? ""
                let petName = data["petName"] as! String? ?? ""
                let infoPet = paw(id: pid, petName: petName, imagenMascota:photo1)
                self.pets.insert(infoPet, at: 0)
            }
        }
    }
}

如果您还需要代码中的其他内容,请告诉我,因为我在这里也很新,而且我不知道要问什么。

3 个答案:

答案 0 :(得分:1)

完成处理程序是一些需要一些时间才能完成工作的函数的代码,您不需要继续等待就可以完成它。

例如,如果您的邮箱离我们很远,您就送孩子去取邮件。他们需要花一分钟时间才能转到邮箱,获取邮件,然后再回来,所以您将继续进行其他操作。由于孩子们很容易忘记,您将它们带回后,会在纸上写下一些说明如何处理。那张纸是完成处理程序。这张纸上的指示中最有可能的指示是将部分或全部邮件交给您,但也许包括首先过滤掉所有垃圾邮件或其他内容。

在您共享的代码中,您实际上有两个完成处理程序;在db.collection("users").document(uid!).getDocument开始的行和self.db.collection("pets").document(pet).getDocument开始的行中,{ }内部的所有内容都是完成处理程序(或闭包)。

为了将结果输入到表中,您可能需要进行两项更改。第一个更改是将}self.petslist = document["petslist"] as? Array ?? [""]行之后移到self.pets.insert(infoPet, at: 0)行之后。

第二个更改是移动:

DispatchQueue.main.async {
    self.tableView.reloadData()
}

viewDidLoad()loadPets(),并放在self.pets.insert(infoPet, at: 0)行之后。这样,所有数据输入并处理后,表视图将重新加载。

现在,您要告诉孩子去拿邮件并将其放在篮子里,但是当孩子刚从前门出来时试图将邮件从篮子里拿出来,您需要等到孩子回来,然后从篮子里拿出邮件。

答案 1 :(得分:1)

您不需要完成处理程序,需要DispatchGroup在上一次获取之后重新加载表视图。

并且您必须将循环放入第一次数据库访问的完成处理程序中

override func viewDidLoad() {
    super.viewDidLoad()
    self.loadPets()
}

func loadPets(){
    let group = DispatchGroup()
    let photo1 = UIImage(named: "Log")
    db = Firestore.firestore()

    db.collection("users").document(uid!).getDocument { documentSnapshot, error in
        guard let document = documentSnapshot else { return }

        self.petslist = document["petslist"] as? [String] ?? []
        if self.petslist.isEmpty {
            DispatchQueue.main.async {
               self.tableView.reloadData()
            }
            return
        }

        for pet in self.petslist {
            group.enter()
            self.db.collection("pets").document(pet).getDocument { documentSnapshot, error in
                defer { group.leave() }

                guard let document = documentSnapshot,
                   let data = document.data() else { return }

                let pid = data["pid"] as? String ?? ""
                let petName = data["petName"] as? String ?? ""
                let infoPet = paw(id: pid, petName: petName, imagenMascota:photo1)
                self.pets.insert(infoPet, at: 0)
           }
        }
        group.notify(queue: .main)  {
           self.tableView.reloadData()
        }
    }
}

答案 2 :(得分:1)

让我们保持这种超级简单-可能不需要为此任务利用完成处理程序和调度组。

这是问题所在:Firestore是异步的,您只能在调用后的闭包内使用返回的数据。该循环之后的代码实际上将在循环中的代码之前执行,因为代码比Internet快。例如。

db.collection("users").document(uid!).getDocument... {
    //data is valid here
    print("Test")
}
print("Hello") //this code will execute and print Hello before Test is printed

因此,这是一个简单的解决方案,可加载用户数据,然后获取其宠物名称。我们有两个收藏夹

users
   uid_0
      name: "Leroy"
      petslist:
         0: "pet_0"
         1: "pet_1"
         2: "pet_2"
pets
   pet_0
      petName: "Spot"
   pet_1
      petName: "Rover"
   pet_2
      petName: "Fluffy"

和代码

func loadPets() {
    let uid = "uid_1"
    self.db.collection("users2").document(uid).getDocument { documentSnapshot, error in
        guard let document = documentSnapshot else {
            return
        }
        let name = document["name"] as! String
        print("Owner \(name) has the following pets:")
        let petList = document["petslist"] as? Array ?? [""]
        for pet in petList {
            self.db.collection("pets").document(pet).getDocument { documentSnapshot, error in
                guard let document = documentSnapshot else {
                    return
                }
                let petName = document.get("petName") as? String ?? "No Pet Name"
                print(petName) //Or reload the tableview**
            }
        }
    }
}

和输出

Owner Leroy has the following pets:
Spot
Rover
Fluffy

话虽如此,您可以将闭包作为一种选择来做同样的事情,但我认为这超出了问题的范围。

**在此示例中,我保持简单。实际上,如果您有很多数据,则在加载每个宠物之后刷新tableView将导致闪烁,并且不是良好的UI体验。这是DispatchGroup可以发挥作用的地方,例如,您可以加载所有宠物,当最后一个宠物被加载时,离开调度组并然后更新tableView。

@Vadian在他的答案中概述了一个DispatchGroups版本,但这是使用DispatchGroup加载宠物名称的一种变体:

func loadPets() {
    let uid = "uid_1"

    self.db.collection("users2").document(uid).getDocument { documentSnapshot, error in
        guard let document = documentSnapshot else { return }
        let name = document["name"] as! String
        print("Owner \(name) has the following pets:")

        let group = DispatchGroup()

        let petList = document["petslist"] as? Array ?? [""]

        for pet in petList {
            group.enter()
            self.db.collection("pets").document(pet).getDocument { documentSnapshot, error in
                guard let document = documentSnapshot else { return }
                let petName = document.get("petName") as? String ?? "No Pet Name"
                print(petName)
                group.leave()
            }
        }

        group.notify(queue: .main) {
            print("all pets loaded, reload tableview")
        }
    }
}