Como executar uma Firestore consulta dentro de uma função de mapa na Swift

0

Pergunta

Eu sou novo para SwiftUI e Firebase e eu estou tentando construir o meu primeiro aplicativo. Eu sou o armazenamento de Jogo documentos em Firestore e um dos campos é uma matriz contendo os ids de usuário de jogadores como você pode ver na imagem.

Jogo de estrutura de dados

Dito isto, eu estou tentando lista de todos os jogos de um determinado usuário e ter todos os jogadores listados em cada uma das células (a ordem é importante).

Para criar a lista de jogos na INTERFACE do usuário que eu criei um GameCellListView e um GameCellViewModel. O GameCellViewModel deve carregar os jogos e a matriz de usuários que correspondem aos jogadores de cada jogo. No entanto, eu não estou sendo capaz de carregar os usuários para uma matriz. Eu tenho que ir pelos jogadores matriz e consulta o banco de dados para cada Identificação e acrescentar a um Usuário matriz; em seguida, eu deveria ser capaz de retornar a este Usuário matriz. Desde que eu estou usando um loop for, eu não posso atribuir os valores para a matriz e, em seguida, devolvê-lo. Eu tentei usar map(), mas eu não posso executar uma consulta dentro de ti. O objetivo é a carga de que "todos" var com uma estrutura que recebe de um jogo e de seus jogadores GamePlayers(players: [User], game: Game)

Ele deve parecer algo como o fragmento de código abaixo, mas os usuários matriz sempre vem vazio. Esta função é executada em GameCellViewModel init. Eu espero que você possa entender o meu problema e agradeço antecipadamente! Foi preso a este por 2 semanas de agora

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

Melhor resposta

0

Há um monte de peças móveis no seu código e assim isolar pontos de falha exigiria ver código adicional para apenas estar ciente de que a inicial. Dito isso, se você é relativamente novo para o Firestore ou Swift, em seguida, eu fortemente sugiro que você primeiro obter uma alça sobre esta função usando a sintaxe básica. Uma vez que você está confortável com o ins-e-saídas de loop assíncrono, em seguida, eu gostaria de sugerir a refatoração de código usando as mais avançadas sintaxe, como você tem aqui.

Sua função requer a execução assíncrona de trabalho dentro de cada iteração do loop (de cada documento). Você realmente precisa fazer isso duas vezes, async funciona dentro de um loop dentro de um loop. Certifique-se de que este é o que você realmente quer fazer antes de prosseguir, porque não pode haver formas mais claras, o que pode incluir uma mais eficiente de dados NoSQL arquitetura. Independentemente, para os fins desta função, a começar com o mais básico de sintaxe não é para o trabalho, que é o Envio de Grupo em conjunto com o para-loop. Vá em frente e ninho estes até que você o tenha de trabalho e, em seguida, considerar a refatoração.

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)
        }
    }
}

Notei também que no segundo conjunto de assíncrona tarefas dentro do segundo ciclo, você está adicionando instantâneo ouvintes. Você tem certeza que você quer fazer isso? Não você só precisa de um simples documento obter?

2021-11-23 16:44:21

Obrigado por sua ajuda! Vou implementar isso em poucas horas e verifique se ele funciona para o meu. Eu costumava despachar os grupos de uma vez e ele congelou o aplicativo, mas foi um pouco diferente da sua sugestão. Você poderia fornecer o "correto" de fazer isso? Mesmo se ele necessita de alterar a estrutura de dados. Posso incluir mais de código, para que você possa ter uma melhor compreensão. Obrigado novamente!
Álvaro Miguel Samagaio

Em outros idiomas

Esta página está em outros idiomas

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................