Cómo ejecutar un Firestore consulta dentro de una función de mapa en Swift

0

Pregunta

Soy nuevo en SwiftUI y Firebase y estoy tratando de crear mi primera aplicación. Yo soy el almacenamiento de documentos del Juego en Firestore y uno de los campos es una matriz que contiene los identificadores de usuario de los jugadores, como se puede ver en la imagen.

Juego de estructura de datos

Dicho esto, estoy tratando de lista de todos los juegos de un determinado usuario, y tener a todos los jugadores mencionados en cada una de las células (el orden es importante).

Con el fin de crear la lista de juegos en la interfaz de usuario que he creado un GameCellListView y un GameCellViewModel. El GameCellViewModel debe cargar los juegos y la variedad de usuarios que corresponden a los jugadores de cada juego. Sin embargo, yo no estoy siendo capaz de cargar a los usuarios a una matriz. Tengo que ir a través de los jugadores de la matriz y de consulta de la base de datos para cada Id y anexar a una matriz de Usuario; a continuación, debería ser capaz de devolver esta matriz de Usuario. Ya estoy usando un bucle for, yo no puedo asignar los valores a la matriz y, a continuación, volver. He intentado utilizar map(), pero no puedo realizar una consulta dentro de ella. El objetivo es cargar de que "todos" var con una estructura que recibe un juego y sus jugadores GamePlayers(players: [User], game: Game)

Debe parecerse a la siguiente fragmento de código, pero los usuarios de la matriz siempre viene vacío. Esta función se ejecuta en GameCellViewModel init. Espero que puedan entender mi problema y gracias de antemano! Ha atascado en esto durante 2 semanas

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

Mejor respuesta

0

Hay un montón de piezas móviles en su código, y para aislar los puntos de fracaso sería necesario ver código adicional así que acaba de ser conscientes de que por adelantado. Dicho esto, si usted es relativamente nuevo en Firestore o Swift, a continuación, me gustaría sugerir fuertemente que usted primero conseguir una manija en esta función se utiliza la sintaxis basic. Una vez que estés cómodo con el ins y las salidas de async bucle, a continuación, me permito sugerir la refactorización de código utilizando la más avanzada de la sintaxis, como la que tenemos aquí.

Su función requiere la ejecución asíncrona de trabajo dentro de cada iteración del bucle (de cada documento). Usted realmente necesita para hacer esto dos veces, async trabajo dentro de un bucle dentro de un bucle. Estar seguro de que es lo que realmente quiero hacer antes de continuar, porque no puede ser más limpio formas, que puede incluir una más eficiente arquitectura de datos NoSQL. Independientemente, para los fines de esta función, inicie con la más básica de la sintaxis no es para el trabajo, que es el Grupo de Despacho en concierto con la for-loop. Vaya por delante y el nido hasta que usted tiene que trabajar y, a continuación, considerar la refactorización.

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

También me di cuenta de que en el segundo conjunto de tareas de async dentro de la segunda vuelta, va a añadir instantánea de los oyentes. ¿Estás realmente seguro de que quieres hacer esto? No sólo se necesita de un documento plano de conseguir?

2021-11-23 16:44:21

Gracias por su ayuda! Voy a implementar esto en un par de horas y compruebe si funciona para mi. He utilizado envío de grupos una vez y se congeló la aplicación, pero era un poco diferente de su propuesta. Podría proporcionar la manera "correcta" de hacerlo? Incluso si se requiere cambiar la estructura de datos. Puedo incluir más de código, de modo que usted puede tener una mejor comprensión. Gracias de nuevo!
Álvaro Miguel Samagaio

En otros idiomas

Esta página está en otros idiomas

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