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