Comment faire pour exécuter un Firestore requête à l'intérieur d'une carte de fonction en Swift

0

La question

Je suis nouveau sur SwiftUI et Firebase et je suis en train de créer ma première application. Je suis le stockage de Jeu de documents dans Firestore et l'un des champs est un tableau contenant les identifiants des joueurs comme vous pouvez le voir dans l'image.

Jeu de structure de données

Cela étant dit, je suis en train de la liste de tous les jeux d'un utilisateur donné et disposent de tous les joueurs inscrits dans chacune des cellules (l'ordre est important).

Afin de créer la liste des jeux dans l'INTERFACE utilisateur, j'ai créé un GameCellListView et un GameCellViewModel. Le GameCellViewModel doit se charger à la fois les jeux et le tableau des utilisateurs qui correspondent aux joueurs de chaque jeu. Cependant, je ne suis pas être en mesure de charger les utilisateurs à un tableau. Je dois passer par les joueurs de la matrice de requête et de la base de données pour chaque Id et l'ajouter à un Utilisateur de tableau; alors je devrais être en mesure de retourner cette matrice Utilisateur. Depuis que je suis en utilisant une boucle for, je ne peux pas assigner les valeurs dans le tableau, puis le retourner. J'ai essayé avec la fonction map(), mais je ne peux pas effectuer une requête à l'intérieur d'elle. Le but est de charger que "toutes les" le var avec une structure qui reçoit un jeu et de ses joueurs GamePlayers(players: [User], game: Game)

Il devrait ressembler à l'extrait de code ci-dessous, mais les utilisateurs de tableau vient toujours vide. Cette fonction s'exécute sur GameCellViewModel init. J'espère que vous pouvez comprendre mon problème et je vous remercie à l'avance! Été coincé sur ce pour 2 semaines maintenant

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

La meilleure réponse

0

Il y a beaucoup de pièces en mouvement dans votre code et ainsi d'isoler les points de défaillance aurait besoin de voir un code supplémentaire si juste être conscient de cela à l'avance. Cela dit, si vous êtes relativement nouveau à Firestore ou Swift alors je vous suggère fortement de la première à obtenir une poignée sur cette fonction à l'aide de la syntaxe de base. Une fois que vous êtes à l'aise avec les tenants et les aboutissants de l'asynchrone en boucle alors je suggère de refactoring du code en utilisant les plus avancés de la syntaxe, comme vous l'avez ici.

Votre fonction nécessite l'exécution asynchrone de travail au sein de chaque itération de boucle (de chaque document). Vous avez réellement besoin de le faire deux fois, async travail au sein d'une boucle dans une boucle. Vous assurer que c'est vraiment ce que vous voulez faire avant de poursuivre car il peut être plus propre les moyens, ce qui peut inclure un plus efficace NoSQL architecture de données. Peu importe, pour l'application de cette fonction, commencez par le plus de base de la syntaxe, il est pour le travail qui est de l'Envoi Groupe de concert avec la boucle for. Aller de l'avant et nest ces jusqu'à ce que vous avez de travail et d'examiner ensuite le 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)
        }
    }
}

J'ai aussi remarqué que dans la deuxième série de async tâches au sein de la deuxième boucle, vous êtes l'ajout instantané auditeurs. Êtes-vous vraiment sûr de vouloir faire cela? Ne pas vous avez juste besoin d'un simple document d'obtenir?

2021-11-23 16:44:21

Merci pour votre aide! Je vais mettre en œuvre dans quelques heures et vérifier si cela fonctionne pour moi. J'ai utilisé le dispatching des groupes une fois et il a gelé l'application, mais il était légèrement différente de votre suggestion. Pourriez-vous donner la "bonne" façon de faire cela? Même si elle nécessite une modification de la structure de données. Je peux inclure plus de code, de sorte que vous pouvez avoir une meilleure compréhension. Merci encore!
Álvaro Miguel Samagaio

Dans d'autres langues

Cette page est dans d'autres langues

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