Как правильно пройти через Firebase Query с обработчиками завершения

Проблема:

Мне нужно пройти через вызов запроса 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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я предполагаю, что вы хотите объединить результаты всех сообщений пользователей и вернуть их в виде единого массива сообщений.

Когда вы вызываете 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)
    }
}

Дополнительные примечания

  1. Swift async/await — это более современный способ обработки асинхронных задач, подобных этой, без некоторых подводных камней этого решения, на которое стоит обратить внимание.

  2. Выполнение отдельного запроса Firestore для каждого пользователя для получения всех его данных не очень эффективно. Это связано с тем, что каждый запрос требует отдельного сетевого запроса и будет стоить вам как минимум один документ, прочитанный за каждый сделанный запрос, независимо от того, есть ли результаты для каждого пользователя или нет. Вам следует подумать о структурировании данных в базе данных Firestore, чтобы вы могли вернуть все необходимые данные в одном запросе. Это может потребовать денормализации некоторых ваших данных, чтобы обеспечить более эффективные запросы.

Большое спасибо за ваши знания, вклад и помощь в моем обучении.

husharoonie 22.12.2022 01:02

Другие вопросы по теме