swift for循环数据顺序不正确
Posted
技术标签:
【中文标题】swift for循环数据顺序不正确【英文标题】:swift for loop order of data is not right 【发布时间】:2021-05-09 07:14:23 【问题描述】:我想从 firebase 获取数据并将它们放入一个数组中。函数的第一部分总是按正确的顺序排列,当我打印时我可以看到它(调试(文件)。但是在 for 循环之后,文档的顺序混乱,我总是得到随机顺序。我不应该总是得到相同的顺序?
func getUnreadMessages()
guard let uid = AuthViewModel.shared.userSession?.uid else return
Firestore.firestore().collection("users").document(uid).collection("chats").order(by: "created", descending: true).getDocuments (snapshot, _) in
guard let files = snapshot?.documents.compactMap( $0.documentID ) else return
print("DEBUG: \(files)")
for file in files
Firestore.firestore().collection("users").document(uid).collection("chats").document(file).collection("messages").whereField("read", isEqualTo: false).getDocuments (snapshot, _) in
guard let documents = snapshot?.documents.compactMap( $0.documentID ) else return
print("DEBUG: \(documents)")
self.count.append(documents.count)
print("DEBUG: \(self.count)")
【问题讨论】:
您正在进行异步调用,因此无法保证您收到回复的顺序。 “我不应该总是得到相同的订单吗?”不,你不应该编写依赖于顺序的代码。 该代码一遍又一遍地读取同一个集合。例如它读取.collection("users").document(uid).collection("chats")
,这意味着集合文档在代码中可用。然后它在一个紧密的循环中再次读取同一个集合;没有理由这样做。读取集合后,可以迭代该集合(快照)中的文档。似乎尝试的任务是获取文档计数?如果是这样,函数名称不应该是getUnreadMessages
,因为这不是它正在做的事情。能否澄清一下问题和代码?
【参考方案1】:
您会得到不同的结果顺序,因为当您以正确的顺序调用数据库时,不能保证数据库会以相同的顺序返回您的调用,因为某些调用比其他调用花费的时间更长。我认为最简单的解决方案是记录原始顺序,将其附加到您的第二次调用中的数据(您确定文档计数的位置),然后按原始顺序对集合(数组)进行排序。
将此索引值附加到文档计数的最简单方法是自定义模型:
struct MessageCount
let count: Int // this is the message count you're after
let n: Int // this is the index of the original order
init(count: Int, n: Int)
self.count = count
self.n = n
然后只需使用调度组来协调异步任务,并在调度组完成后,按index
对数组进行排序,您将获得按预期顺序排列的消息计数数组:
func getUnreadMessages()
guard let uid = AuthViewModel.shared.userSession?.uid else
return
let db = Firestore.firestore() // instantiate it once since it could be created hundreds or thousands of times in this function
db.collection("users").document(uid).collection("chats").order(by: "created", descending: true).getDocuments (snapshot, error) in
guard let snapshot = snapshot,
!snapshot.isEmpty else
if let error = error
print(error) // you oddly omitted the error in your code, never do that
return
let dispatch = DispatchGroup() // set up the dispatch group outside the loop
var messageCounts = [MessageCount]() // this temp array will carry the data with the index
// to record the original order of the loop, just enumerate it and access `n` (the index)
for (n, doc) in snapshot.documents.enumerated()
dispatch.enter() // enter dispatch on each iteration
db.collection("users").document(uid).collection("chats").document(doc.documentID).collection("messages").whereField("read", isEqualTo: false).getDocuments (snapshot, error) in
if let snapshot = snapshot
let c = snapshot.count // get the message count
let count = MessageCount(count: c, n: n) // add it to the model along with n which is captured by the parent closure
messageCounts.append(count) // append to our temp array
else if let error = error
print(error)
dispatch.leave() // leave dispatch no matter the outcome
// this is the completion handler of the dispatch group
dispatch.notify(queue: .main)
// sort the array by index and then map it to just get the message counts
let counts = messageCounts.sorted(by: $0.n < $1.n ).map( $0.count )
【讨论】:
【参考方案2】:返回结果的顺序由order(by
子句确定。否则结果可能看起来有些随机。
在这种情况下,第一个 Firebase 调用指定了一个顺序,因此这些文档将始终以正确的顺序返回。
collection("chats").order(by: "created"
但是下一个 firebase 调用没有指定顺序,所以返回的文档可能有点不一致。
.collection("messages").whereField
我们需要有一些方法来保证这个顺序。
假设结构是这样的
chats (collection)
user ids (documents)
chats (collection)
chat ids (documents)
messages (collection)
message ids (documents that you want ordered)
消息 ID 需要有一个字段来排序它们 - 调用 ordering
这是打印每个聊天 id 中消息数量的计数然后按顺序打印消息的代码
func getUnreadMessages()
let uid = "uid_0"
self.db.collection("users_chats").document(uid).collection("chats").getDocuments(completion: snapshot, error in
if let err = error
print(err.localizedDescription)
return
guard let docs = snapshot?.documents else return
for doc in docs
let ref = doc.reference.collection("messages")
ref.order(by: "ordering").getDocuments(completion: messagesSnapshot, error in
if let err = error
print(err.localizedDescription)
return
guard let messages = messagesSnapshot?.documents else return
print("the chat document: \(doc.documentID) has \(messages.count) messages")
for msg in messages
let order = msg.get("ordering")
let msg = msg.get("read")
print("order: \(order!)", "is read: \(msg!)")
)
)
如果聊天 0 中有 3 条消息,输出如下所示
the chat document: chat_0 has 3 messages
the chat document: chat_1 has 0 messages
the chat document: chat_2 has 0 messages
order: 0 isRead: 0
order: 1 isRead: 1
order: 2 isRead: 0
【讨论】:
以上是关于swift for循环数据顺序不正确的主要内容,如果未能解决你的问题,请参考以下文章
使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用