Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Swift中使循环函数异步?_Swift_Firebase_Asynchronous - Fatal编程技术网

如何在Swift中使循环函数异步?

如何在Swift中使循环函数异步?,swift,firebase,asynchronous,Swift,Firebase,Asynchronous,我正在为一个库创建一个应用程序。我试图获取用户从Firebase签出的所有书籍,但我试图使函数与DispatchGroup异步,但似乎没有起作用。我怀疑这是因为在函数内部找到了for in循环 func fetchHistory() { if items.count > 0 { items.removeAll() } let myGroup = DispatchGroup() myGroup.enter() var it

我正在为一个库创建一个应用程序。我试图获取用户从Firebase签出的所有书籍,但我试图使函数与DispatchGroup异步,但似乎没有起作用。我怀疑这是因为在函数内部找到了for in循环

    func fetchHistory() {

    if items.count > 0 {
        items.removeAll()
    }

    let myGroup = DispatchGroup()
    myGroup.enter()

    var itemNames = [String]() // this holds the names of the child values of /users/uid/items/ <-- located in Firebase Database
    guard let uid = fAuth.currentUser?.uid else {return}
    fData.child("users").child(uid).child("items").observe(.value, with: { snapshot in

        // make sure there is at least ONE item in the history
        if snapshot.childrenCount > 0 {
            let values = snapshot.value as! NSDictionary
            for i in values.allKeys {
                itemNames.append(i as! String)
            }

            print(itemNames)
            let uid = fAuth.currentUser!.uid // get the UID of the user
            for item in itemNames {
                fData.child("users").child(uid).child("items").child(item).observe(.value, with: { snapshot in
                    let values = snapshot.value as! NSDictionary
                    let bookTitle = values["title"] as! String
                    print(bookTitle)
                    let bookAuthor = values["author"] as! String
                    print(bookAuthor)
                    let bookCoverUrl = values["coverUrl"] as! String
                    print(bookCoverUrl)
                    let bookStatus = values["status"] as! String
                    print(bookStatus)
                    let bookDueDate = values["dueDate"] as! String
                    print(bookDueDate)

                    let book = Book(name: bookTitle, author: bookAuthor, coverUrl: bookCoverUrl, status: bookStatus, dueDate: bookDueDate)
                    self.items.append(book)
                })
            }
            self.booksTable.isHidden = false
        } else {
            self.booksTable.isHidden = true
        }

    })

    myGroup.leave()
    myGroup.notify(queue: DispatchQueue.main, execute: {
        self.booksTable.reloadData()
        print("Reloading table")
    })

}
输出的前两行应该在其他所有内容打印完毕后打印。在这方面我真的需要一些帮助,我已经被困了好几个小时了。谢谢

编辑: 根据要求,以下是我的Firebase结构:

users:
  meZGWn5vhzXpk5Gsh92NhSasUPx2:
    ID: "12345"
    firstname: "Faraaz"
    items:
        78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8
            author: "Suzanne Collins"
            coverUrl: "https://images.gr assets.com/books/1358275419s/..."
            dueDate: "Date"
            status: "Checked"
            title: "Mockingjay (The Hunger Games, #3)"
     type: "regular"
有几个问题:

  • 模式是必须在异步调用的完成处理程序内调用
    leave
    。您希望这是在闭包中执行的最后一件事,因此可以将其添加为完成处理程序闭包中的最后一行

    或者我更喜欢使用
    defer
    子句,这样您不仅知道这将是闭包中执行的最后一件事,而且还知道:

    • 你确保你
      离开
      ,即使你后来在你的关闭中添加了任何“提前退出”;及
    • 在代码中,
      enter
      leave
      调用直观地显示在彼此的旁边,这样您就不必在闭包的底部进行直观搜索,以确保正确调用了闭包
  • 此外,如果要在
    for
    循环中等待异步调用,也必须将其添加到那里

  • 这是一个很小的问题,但您可能希望在成功打开
    uid
    之前不创建组。如果可以
    返回
    而不执行任何异步代码,为什么要创建
    调度组

  • 因此,也许:

    func fetchHistory() {
    
        if items.count > 0 {
            items.removeAll()
        }
    
        var itemNames = [String]()
        guard let uid = fAuth.currentUser?.uid else {return}
    
        let group = DispatchGroup()
    
        group.enter()
    
        fData.child("users").child(uid).child("items").observe(.value, with: { snapshot in
    
            defer { group.leave() }               // in case you add any early exits, this will safely capture
    
            if snapshot.childrenCount > 0 {
                ...
                for item in itemNames {
    
                    group.enter()                 // also enter before we do this secondary async call
    
                    fData.child("users").child(uid).child("items").child(item).observe(.value, with: { snapshot in
    
                        defer { group.leave() }   // and, again, defer the `leave`
    
                        ...
                    })
                }
                ...
            } else {
                ...
            }
        })
    
        group.notify(queue: .main) {
            self.booksTable.reloadData()
            print("Reloading table")
        }    
    }
    

    虽然Rob给出了一个绝妙的答案,但我会从另一个方向来寻找解决方案

    一本书一次只能有一个人借阅,但借阅者可以有多本书。由于这种关系,只需将拥有此书的人与此书本身结合起来:

    下面是一个建议的用户结构

    users
      uid_0
        name: "Rob"
      uid_1
        name: "Bill"
    
    然后是图书节点

    books
       78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8
         author: "Suzanne Collins"
         coverUrl: "https://images.gr assets.com/books/1358275419s/..."
         dueDate: "Date"
         status: "Checked"
         title: "Mockingjay (The Hunger Games, #3)"
         checked_out_by: "uid_0"
         check_date: "20180118"
    
    然后,获取Rob已签出的所有书籍,并使用这些结果填充数组并在tableview中显示它变得非常简单:

    //var bookArray = [Book]() //defined as a class var
    
    let booksRef = self.ref.child("books")
    let query = booksRef.queryOrdered(byChild: "checked_out_by").queryEqual(toValue: "uid_0")
    booksRef.observeSingleEvent(of: .value, with: { snapshot in
        for child in snapshot.children {
            let snap = child as! DataSnapshot
            let book = Book(initWithSnap: snap) //take the fields from the snapshot and populate the book
            self.bookArray.append(book)
        }
        self.tableView.reloadData()
    })
    
    但是你会问自己,“赛尔夫,如果我想要一份谁签了这本书的记录怎么办?”

    如果您需要该功能,只需对books节点稍作更改,就可以利用深度查询

    books
       78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8
         author: "Suzanne Collins"
         coverUrl: "https://images.gr assets.com/books/1358275419s/..."
         dueDate: "Date"
         status: "Checked"
         title: "Mockingjay (The Hunger Games, #3)"
         check_out_history
            "uid_0" : true
            "uid_1" : true
    
    并将签出日期移动到“用户”节点。然后,您可以查询任何书籍的任何用户,并具有签出该书籍的用户的历史记录。(需要有逻辑来确定谁目前拥有这本书,所以这只是一个起点)

    或者,如果需要其他选项,请保留一个单独的“书本历史记录”节点

    book_history
       78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8
          -9j9jasd9jasjd4    //key is created with childByAutoId
             uid: "uid_0"
             check_out_date: "20180118"
             check_in_date: "20180122"
             condition: "excellent"
          -Yuhuasijdijiji    //key is created with childByAutoId
             uid: "uid_1"
             check_out_date: "20180123"
             check_in_date: "20180125"
             condition: "good"
    

    其概念是让Firebase为您完成工作,而不是反复迭代数组,并且必须发出几十个调用才能获得所需的数据。调整结构使将来的维护和扩展变得更加简单,并且避免了异步代码的所有问题,因为它都在闭包中;漂亮整洁。

    可能有更好的方法来获取您想要的数据,而不必创建这样的循环。使用正确的Firebase结构,您应该能够执行单个查询并返回用户已签出的所有书籍。您能否包含Firebase结构的文本片段,以便我们了解Firebase中当前存储的内容?您可能会希望扇出/取消规范化您的结构,这将非常有帮助。与您的主要问题无关,请注意,如果您决定使用上述模式,我建议不要将
    Book
    值直接异步附加到驱动表视图的主模型对象。您应该填充一个本地数组,然后只更新
    notify
    块中的模型对象。现在,如果用户碰巧在附加了一些记录之后但在最终的
    重新加载数据之前滚动,您可能会得到关于表视图和模型状态不一致的错误。@Jay我已经更新了帖子。我花了很多时间试图找出一种更好/更干净的方法,但我就是找不到一种方法。这真的是万不得已的办法。@Rob非常感谢您的帮助,我将进行相应的编辑。@Rob的答案非常棒,技术上几乎完美,我添加了另一个答案作为替代方法(供未来读者使用)。我认为从长远来看,你会想调整你的结构,以更好地适应你的要求。让Firebase飞起来的一部分是数据的非规范化、数据的扇形化以及减少观察和查询。因此,与其通过查询/观察1000次来获取1000条数据,不如用一次观察或一次查询来获取1000条数据-这样更有效,并将显著增加用户的UI。只是想一想:-)谢谢你的帮助,但是你能解释一下
    defer
    子句到底是什么吗“使用
    defer
    编写一个代码块,该代码块在函数返回之前,在函数中所有其他代码之后执行。无论函数是否抛出错误,都会执行代码。您可以使用“延迟”将安装和清理代码彼此相邻地编写,即使它们需要在不同的时间执行。“这基本上是一种很好、简单的方法,可以指定退出当前作用域时将执行的代码块,无论您是如何执行的(例如,是否正常退出、在
    guard
    语句中提前退出、抛出错误等)。非常感谢您的帮助。
    book_history
       78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8
          -9j9jasd9jasjd4    //key is created with childByAutoId
             uid: "uid_0"
             check_out_date: "20180118"
             check_in_date: "20180122"
             condition: "excellent"
          -Yuhuasijdijiji    //key is created with childByAutoId
             uid: "uid_1"
             check_out_date: "20180123"
             check_in_date: "20180125"
             condition: "good"