我正在使用Firebase的Firestore来存储我的iOS应用程序的数据。在Firestore中,我有两个集合。一个叫songs
,一个叫playlists
。 songs
集合包含许多文档,每个文档内部都有一个歌曲信息。这是songs
文档的样子。
然后,在我的收藏playlists
它包含一些文件是播放列表。这是一个playlists
文档的示例。
我希望能够显示播放列表,然后从我的songs
集合中获取该播放列表中的songTitles。所以,我做到了这一点:
var testPlaylistTitles = [String]()
db.collection("playlists").whereField("title", isEqualTo: "Test Playlist").getDocuments { (snapshot, error) in
for document in (snapshot?.documents)! {
self.testPlaylistTitles = ((document.data()["songTitles"] as? [String])!)
}
}
这使得变量testPlaylistTitles
= ["Test Song", "Test Song 2", "Test Song 3"]
。这太好了。但这是出错的地方。我想从我的songs
系列中获取有关这些歌曲的信息。因此,我创建了一个for循环并循环播放歌曲以附加到我的其他艺术家,图像和标题阵列。就这个例子而言,我将使用returedTitles
的变量。
var testPlaylistTitles = [String]()
db.collection("playlists").whereField("title", isEqualTo: "Test Playlist").getDocuments { (snapshot, error) in
for document in (snapshot?.documents)! {
testPlaylistTitles = ((document.data()["songTitles"] as? [String])!)
}
var returedTitles = [String]()
for i in 0...testPlaylistTitles.count-1 {
db.collection("songs").whereField("title", isEqualTo: testPlaylistTitles[i]).getDocuments(completion: { (snapshot, error) in
for document in (snapshot?.documents)! {
returedTitles.append((document.data()["title"] as? String)!)
}
})
}
}
现在,returedTitles
等于原始testPlaylistTitles
的洗牌版本。我一开始认为它不知何故是按字母顺序排序testPlaylistTitles
,但事实并非如此。有没有人对正在发生的事情以及如何解决它有任何想法!非常感谢!这一直困扰着我。
我不知道如何在swift中执行此操作,但我会将播放列表文档中歌曲的数据结构更改为歌曲为键的对象,然后在查询的第二个循环中将值更改为值,您使用键返回值,您应该能够按值排序。
{
description: "test",
songs: {
"Test Song 1": 1,
"Test Song 2": 3,
"Test Song 4": 2
},
title: "Test playlist"
}
我还可以建议使用列表中歌曲的ID,因为您在以后的某个阶段检索数据。
看起来你依赖于for循环中查询的执行顺序。请记住,getDocuments()是异步的,这意味着它会立即返回,并且结果会在一段时间后出现在回调中(无法保证需要多长时间)。因为它是异步的,所以你有效地同时开始查询testPlaylistTitles.count
nubmer,每个查询都不考虑另一个。因此,您正在构建的数组将以未定义的顺序累积结果。为了帮助可视化,请在每个回调中放置一条日志消息,以查看它们实际调用的顺序。每次都包含数组的内容,以查看数组的构建方式。
如果结果的顺序很重要,您需要弄清楚自己的订单是什么。如果这意味着按顺序依次执行查询(这将是整体性能命中),那么您将必须以这种方式编码。
但最重要的是,你需要仔细考虑做异步工作。有关Firebase API异步原因的详细信息,请访问Please read this blog。
正如Doug指出的那样,问题是调用是异步的,因此它们不一定按照启动它们的顺序添加到数组中 - 它们在数据可用时添加。有几种方法可以解决这个问题,但是一个让你开始的建议是初始化你的数组,使它们的大小等于testPlaylistTitles
的大小。然后,当您获得数据时,将其插入数组中的特定位置,而不是将其附加到数组的末尾。
var testPlaylistTitles = [String]()
db.collection("playlists").whereField("title", isEqualTo: "Test Playlist").getDocuments { (snapshot, error) in
for document in (snapshot?.documents)! {
testPlaylistTitles = ((document.data()["songTitles"] as? [String])!)
}
var returedTitles = [String](repeating: "title", count: testPlaylistTitles.count)
for i in 0...testPlaylistTitles.count-1 {
db.collection("songs").whereField("title", isEqualTo: testPlaylistTitles[i]).getDocuments(completion: { (snapshot, error) in
if (snapshot!.documents.count) > 0 {
let doc = (snapshot?.documents.first)!
returedTitles[i] = ((doc.data()["title"] as? String)!)
}
})
}
}
在这段代码中,我初始化了一个testPlaylistTitles
大小的数组。每个索引的值只是“标题”开头,但是一旦下载,就会被Firestore中的值替换。