Come eseguire un Firestore query all'interno di una funzione di mappa in Swift

0

Domanda

Sono nuovo di SwiftUI e Firebase e sto cercando di costruire la mia prima app. Io sono la memorizzazione di Gioco di documenti in Firestore e uno dei campi è un array che contiene l'id utente dei giocatori, come si può vedere nell'immagine.

I dati di gioco e struttura

Detto questo, sto cercando di elencare tutti i giochi di un determinato utente e di tutti i giocatori elencati in ognuna delle cellule (l'ordine è importante).

Per creare la lista dei giochi in UI ho creato un GameCellListView e un GameCellViewModel. Il GameCellViewModel dovrebbe caricare entrambi i giochi e la matrice di utenti che corrispondono ai giocatori per ogni gioco. Tuttavia, non sono in grado di caricare le utenti di un array. Devo passare attraverso il giocatori array e interrogare il database per ogni Id e aggiungere a un Utente array, quindi dovrei essere in grado di restituire questo Utente array. Dal momento che sto usando un ciclo for, non posso assegnare i valori dell'array e poi tornare. Ho provato con la funzione map(), ma non è possibile eseguire una query all'interno di esso. L'obiettivo è quello di caricare che "tutti" var con una struttura che riceve un gioco e i suoi giocatori GamePlayers(players: [User], game: Game)

Dovrebbe essere qualcosa come il frammento di codice qui di seguito, ma gli utenti array viene sempre vuoto. Questa funzione viene eseguita su GameCellViewModel init. Spero tu possa capire il mio problema e vi ringrazio in anticipo! Stato bloccato su questo per 2 settimane

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

Migliore risposta

0

Ci sono un sacco di parti in movimento nel codice e quindi di isolare i punti di guasto richiederebbe vedere il codice aggiuntivo quindi basta essere consapevoli di questo in anticipo. Detto questo, se siete relativamente nuovi Firestore o Swift poi vorrei consigliamo vivamente prima di ottenere una maniglia su questa funzione utilizzando la sintassi di base. Una volta che sei a tuo agio con i pro e i contro di async loop allora io suggerirei di refactoring del codice di utilizzo più avanzato di sintassi, come avete qui.

La funzione richiede l'esecuzione asincrona di lavoro all'interno di ogni iterazione del ciclo (per ogni documento). Hai veramente bisogno di fare questo due volte, async lavoro all'interno di un ciclo all'interno di un ciclo. Essere sicuri che questo è ciò che si vuole veramente fare, prima di procedere, perché non ci possono essere più puliti modi, che può includere un più efficiente architettura di dati NoSQL. Indipendentemente da ciò, ai fini di questa funzione, iniziare con la maggior parte della sintassi di base c'è per il lavoro che è l'Invio di Gruppo, di concerto con il ciclo for. Vai avanti e nido di questi fino a quando non lavoro e quindi prendere in considerazione di refactoring.

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

Ho anche notato che all'interno della seconda serie di async attività all'interno del secondo ciclo, si sta aggiungendo snapshot ascoltatori. Sei davvero sicuro di voler fare questo? Non basta un normale documento di ottenere?

2021-11-23 16:44:21

Grazie per il vostro aiuto! Io implementare, in poche ore, e verificare se funziona per il mio. Ho usato spedizione gruppi di una volta e gelò l'app, ma è stato leggermente diverso da tuo suggerimento. Potrebbe fornire il modo "giusto" di fare questo? Anche se si richiede di modificare la struttura dei dati. Posso includere più di codice, in modo che si può avere una migliore comprensione. Grazie ancora!
Álvaro Miguel Samagaio

In altre lingue

Questa pagina è in altre lingue

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