Jak uruchomić kwerendę Firestore wewnątrz funkcji karty w Swift

0

Pytanie

Jestem nowy w SwiftUI i Firebase i próbuję stworzyć swoją pierwszą aplikację. Trzymam gier dokumenty w Firestore, i jedno z pól jest tablicą zawierającą identyfikatory użytkowników, graczy, jak widać na obrazku.

Struktura danych gry

Tym nie mniej, staram się wymienić wszystkie gry tego użytkownika i podanie wszystkich graczy w każdej z komórek (kolejność jest ważna).

Aby utworzyć listę gier w interfejsie użytkownika, założyłem GameCellListView i gamecellviewмодель. Gamecellviewмодель musi wczytywać gry, jak i masyw użytkowników, odpowiednich graczy do każdej gry. Jednak nie mogę pobrać użytkowników w tablicę. Muszę przejść przez tablicę graczy i poprosić o bazę danych dla każdego konta i dodać do tablicy użytkowników; wtedy będę mógł odzyskać ten masyw użytkowników. Ponieważ używam pętli for, nie mogę przypisać wartości do tablicy, a następnie go zwrócić. Próbowałem korzystać z map(), ale nie mogę wykonać żądania wewnątrz niego. Celem jest, aby pobrać ten parametr "wszystko" ze strukturą, która podejmuje grę i jej graczy GamePlayers(players: [User], game: Game)

Powinno to wyglądać jak poniżej fragment kodu, ale tablica użytkowników jest zawsze pusta. Funkcja ta uruchamia się przy inicjalizacji GameCellViewModel. Mam nadzieję, że zrozumiecie mój problem i z góry podziękuj ciebie! Zatrzymany na tym już 2 tygodnie

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

Najlepsza odpowiedź

0

W kodzie wiele ruchomych części, więc do izolacji punktów awarii trzeba zobaczyć dodatkowy kod, więc po prostu wcześniej o tym wiedzieć. Niemniej jednak, jeśli jesteś stosunkowo nowy w Firestore lub Swift, gorąco polecam, aby najpierw zrozumieć z tej funkcji, wykorzystując podstawowe składnia. Jak tylko pojawi się komfortowo ze wszystkimi subtelności asynchronicznego cyklu, ja bym proponował przeprowadzić refaktoryzacja kodu z wykorzystaniem bardziej zaawansowanego składni, jak masz tutaj.

Twoja funkcja wymaga wykonywania asynchronicznego pracy w każdej iteracji pętli (każdego dokumentu). Faktycznie musisz zrobić to dwukrotnie, w sposób asynchroniczny do pracy w cyklu w środku cyklu. Upewnij się, że jest to coś, co naprawdę chcesz zrobić, zanim przejdziesz dalej, bo mogą być bardziej czyste sposoby, które mogą obejmować bardziej wydajną architekturę danych NoSQL. Niezależnie od tego, dla celów tej funkcji zacznij od podstawowej składni, który istnieje do zadania, reprezentuje grupę wysyłki wraz z pętlą for. Idź i ucz ich, podczas gdy oni nie zarobią, a następnie pomyśl 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)
        }
    }
}

Zauważyłem również, że w drugim zestawie zadań asynchronicznych w drugim cyklu można dodać detektory migawki. Naprawdę jesteś pewien, że chcesz to zrobić? Czy nie trzeba po prostu prosty dokument?

2021-11-23 16:44:21

Dziękuję za pomoc! Zorbie to przez kilka godzin i sprawdzę, czy to działa dla mnie. Ja kiedyś używałem grupy wysyłki, i to zamroził aplikacja, ale to trochę różniła się od twojej oferty. Mógłbyś podpowiedzieć "właściwy" sposób to zrobić? Nawet jeśli będzie to wymagało zmiany struktury danych. Mogę włączyć więcej kodu, aby można było lepiej zrozumieć. Jeszcze raz dziękuję!
Álvaro Miguel Samagaio

W innych językach

Ta strona jest w innych językach

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