Проблема:
Мне нужно пройти через вызов запроса Firebase.
Мой цикл запускается до вызова Firebase, поскольку он содержит необходимые идентификаторы.
for uid in following_uids {
}
Как правильно зациклить переменную uid
в ссылке и запросе Firebase?
for uid in following_uids {
let fbRef = ref.child("users").child(uid).child("posts")
//query firebase call
}
Репликация:
func getRecentPostsFromFollowers(following_uids: [String]) {
for uid in following_uids {// first loop
let fbRef = ref.child("users").child(uid).child("posts")
fbRef.queryOrderedByKey().queryLimited(toLast: 5).observeSingleEvent(of: .value, with: {snapshot in
if (snapshot.exists()){
let values = snapshot.children.compactMap { ($0 as? DataSnapshot)?.value }
for postData in values {
guard let restDict = (postData as AnyObject) as? [String: Any] else { continue }
//do with data
}
}
})
}//end first loop
print("Completion handler - Loop Done")
}
ПСЕВДОкод:
func getFollowerUIDS(completion: @escaping (_ followers: [String]) -> Void) {
for uid in following_uids {
let fbRef = ref.child("users").child(uid).child("posts")
//query firebase call
}
completion(value)
}
Характеристики:
Версия Xcode 14.2 (14C18) iOS
Я предполагаю, что вы хотите объединить результаты всех сообщений пользователей и вернуть их в виде единого массива сообщений.
Когда вы вызываете observeSingleEvent
ссылку на документ, второй параметр является типом замыкания . По сути, это просто еще одна функция, которую Firebase SDK вызовет, когда данные будут готовы для вас.
Идея заключается в следующем: поскольку получение этих данных может занять некоторое время, мы не хотим блокировать выполнение остальной части вашей функции/кода во время сетевого вызова.
Это означает, что вы, скорее всего, увидите конец цикла "Completion handler - Loop Done"
, вызванного до того, как ваши данные станут доступны.
Это означает, что ваша функция getRecentPostsFromFollowers
вернется до того, как любое из ваших замыканий будет вызвано с данными.
Чтобы позволить вызывающим функциям getRecentPostsFromFollowers
знать, когда данные будут готовы, вы можете добавить свой собственный обработчик завершения, чтобы предоставить им эти данные обратно.
Это замыкание, которое вы вызываете, когда знаете, что все данные готовы.
Однако, поскольку есть несколько замыканий с данными, которые нам нужно объединить, мы можем использовать для этой цели что-то вроде DispatchGroup
.
Мы объединим посты в переменную allPosts
и вернем данные, когда они будут объединены из всех запросов.
Нам нужно заблокировать доступ к
allPosts
, поскольку обработчик завершенияobserveSingleEvent
может работать в любом потоке, и несколько потоков могут одновременно пытаться получить доступ к этой переменной.
typealias PostData = [String: Any]
func getRecentPostsFromFollowers(following_uids: [String], completion: @escaping ([PostData]) -> Void) {
let group = DispatchGroup()
let lock = NSLock()
var allPosts = [PostData]()
for uid in following_uids {
let fbRef = ref.child("users").child(uid).child("posts")
group.enter()
fbRef.queryOrderedByKey().queryLimited(toLast: 5).observeSingleEvent(of: .value, with: {snapshot in
defer { group.leave() }
if (snapshot.exists()){
let values = snapshot.children.compactMap { ($0 as? DataSnapshot)?.value }
lock.lock()
defer { lock.unlock() }
for postData in values {
guard let restDict = (postData as AnyObject) as? PostData else { continue }
allPosts.append(restData)
}
}
})
}
group.notify(queue: .main) {
completion(allPosts)
}
}
Swift async/await — это более современный способ обработки асинхронных задач, подобных этой, без некоторых подводных камней этого решения, на которое стоит обратить внимание.
Выполнение отдельного запроса Firestore для каждого пользователя для получения всех его данных не очень эффективно. Это связано с тем, что каждый запрос требует отдельного сетевого запроса и будет стоить вам как минимум один документ, прочитанный за каждый сделанный запрос, независимо от того, есть ли результаты для каждого пользователя или нет. Вам следует подумать о структурировании данных в базе данных Firestore, чтобы вы могли вернуть все необходимые данные в одном запросе. Это может потребовать денормализации некоторых ваших данных, чтобы обеспечить более эффективные запросы.
Большое спасибо за ваши знания, вклад и помощь в моем обучении.