Как запустить запрос Firestore внутри функции карты в Swift

0

Вопрос

Я новичок в SwiftUI и Firebase и пытаюсь создать свое первое приложение. Я храню игровые документы в Firestore, и одно из полей представляет собой массив, содержащий идентификаторы пользователей игроков, как вы можете видеть на изображении.

Структура игровых данных

Тем не менее, я пытаюсь перечислить все игры данного пользователя и указать всех игроков в каждой из ячеек (порядок важен).

Чтобы создать список игр в пользовательском интерфейсе, я создал GameCellListView и gamecellviewмодель. Gamecellviewмодель должна загружать как игры, так и массив пользователей, соответствующих игрокам каждой игры. Однако я не могу загрузить пользователей в массив. Я должен пройти через массив игроков и запросить базу данных для каждого идентификатора и добавить в массив пользователей; тогда я смогу вернуть этот массив пользователей. Поскольку я использую цикл for, я не могу присвоить значения массиву, а затем вернуть его. Я попытался использовать map(), но я не могу выполнить запрос внутри него. Цель состоит в том, чтобы загрузить этот параметр "все" со структурой, которая принимает игру и ее игроков GamePlayers(players: [User], game: Game)

Это должно выглядеть примерно как приведенный ниже фрагмент кода, но массив пользователей всегда пуст. Эта функция запускается при инициализации GameCellViewModel. Я надеюсь, что вы поймете мою проблему и заранее поблагодарите вас! Застрял на этом уже 2 недели

func loadData() {
        let userId = Auth.auth().currentUser?.uid
        
        db.collection("games")
            .order(by: "createdTime")
            .whereField("userId", isEqualTo: userId)
            .addSnapshotListener { (querySnapshot, error) in
            if let querySnapshot = querySnapshot {
                self.games = querySnapshot.documents.compactMap { document in
                    do {
                        let extractedGame = try document.data(as: Game.self)
                        var user = [User]()
                        let users = extractedGame!.players.map { playerId -> [User] in

                            self.db.collection("users")
                                .whereField("uid", isEqualTo: playerId)
                            .addSnapshotListener { (querySnapshot, error) in
                                guard let documents = querySnapshot?.documents else {
                                    print("No documents")
                                    return
                                }
                                user = documents.compactMap { queryDocumentSnapshot -> User? in
                                    return try? queryDocumentSnapshot.data(as: User.self)
                                    
                                }
                            }
                            return user
                        }
                        
                        self.all.append(GamePlayers(players: users.first ?? [User](), game: extractedGame!))

                        
                        return extractedGame
                    }
                    catch {
                        print(error)
                    }
                    return nil
                }
            }
        }
    }
1

Лучший ответ

0

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

Ваша функция требует выполнения асинхронной работы в каждой итерации цикла (каждого документа). На самом деле вам нужно сделать это дважды, асинхронно работать в цикле внутри цикла. Убедитесь, что это то, что вы действительно хотите сделать, прежде чем продолжить, потому что могут быть более чистые способы, которые могут включать более эффективную архитектуру данных NoSQL. Независимо от этого, для целей этой функции начните с самого базового синтаксиса, который существует для задания, представляющего собой группу отправки совместно с циклом for. Продолжайте и вкладывайте их, пока они у вас не заработают, а затем подумайте о рефакторинге.

func loadData() {
    // Always safely unwrap the user ID and never assume it is there.
    guard let userId = Auth.auth().currentUser?.uid else {
        return
    }
    // Query the database.
    db.collection("games").whereField("userId", isEqualTo: userId).order(by: "createdTime").addSnapshotListener { (querySnapshot, error) in
        if let querySnapshot = querySnapshot {
            // We need to loop through a number of documents and perform
            // async tasks within them so instantiate a Dispatch Group
            // outside of the loop.
            let dispatch = DispatchGroup()
            
            for doc in querySnapshot.documents {
                // Everytime you enter the loop, enter the dispatch.
                dispatch.enter()
                
                do {
                    // Do something with this document.
                    // You want to perform an additional async task in here,
                    // so fire up another dispatch and repeat these steps.
                    // Consider partitioning these tasks into separate functions
                    // for readability.

                    // At some point in this do block, we must leave the dispatch.
                    dispatch.leave()
                } catch {
                    print(error)
                    
                    // Everytime you leave this iteration, no matter the reason,
                    // even on error, you must leave the dispatch.
                    dispatch.leave()
                    
                    // If there is an error in this iteration, do not return.
                    // Return will return out of the method itself (loadData).
                    // Instead, continue, which will continue the loop.
                    continue
                }
            }
            
            dispatch.notify(queue: .main) {
                // This is the completion handler of the dispatch.
                // Your first round of data is ready, now proceed.
            }
        } else if let error = error {
            // Always log errors to console!!!
            // This should be automatic by now without even having to think about it.
            print(error)
        }
    }
}

Я также заметил, что во втором наборе асинхронных задач во втором цикле вы добавляете прослушиватели моментальных снимков. Ты действительно уверен, что хочешь это сделать? Разве вам не нужно просто получить простой документ?

2021-11-23 16:44:21

Спасибо за вашу помощь! Я осуществлю это через несколько часов и проверю, работает ли это для меня. Я однажды использовал группы диспетчеризации, и это заморозило приложение, но оно немного отличалось от вашего предложения. Не могли бы вы подсказать "правильный" способ сделать это? Даже если для этого потребуется изменить структуру данных. Я могу включить больше кода, чтобы вы могли лучше понять. Еще раз спасибо!
Álvaro Miguel Samagaio

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................

Популярное в этой категории

Популярные вопросы в этой категории