Uni_x
Uni_x

Reputation: 326

Swift Array.append in init() not working in combination with Firestore (Google Cloud/FIrebase)

I have tried my first SwiftUI Project. I only want to show some data stored in Firestore (Google Firebase). Here is my code:

import SwiftUI
import FirebaseFirestore
import FirebaseFirestoreSwift

struct MonsterObj: Identifiable, Equatable, Hashable {
    var id = UUID()
    var name: String
    var element: String
    var immune: String
    var size: String
    var twoStarWeakness: String
    var threeStarWeakness: String

    #if DEBUG
    static let exampleMonster = MonsterObj(id: UUID(), name: "Test Monster", element: "Test Element", immun: "Test immun", groesse: "Test groesse", twoStarWeakness: "Test 2 Weakness", threeStarWeakness: "Test3 Weakness")

    #endif

}

class MonsterC: ObservableObject {

    @Published var monsters = [MonsterObj]()

    init() {
        let db = Firestore.firestore()
        var monsterNames: [String] = []

        db.collection("Monster").getDocuments() { (querySnapshot, err) in
            if let err = err {
                print(err)
            } else {
                for document in querySnapshot!.documents {
                    monsterNames.append("\(document.documentID)")
                    print("document: \(document.documentID)")

                }
            }
        }

            for monsterName in monsterNames {
                print(monsterName)
                db.collection("Monster").document(monsterName).getDocument { (document, error) in
                    if let document = document, document.exists {
                        let elementGetter = document.get("element") as! String
                        let immuneGetter = document.get("immune") as! String
                        let sizeGetter = document.get("size") as! String
                        let twoStarWeaknessGetter = document.get("2 star weakness") as! String
                        let threeStarWeaknessGetter = document.get("3 star weakness")as! String

                        self.monsters.append(MonsterObj(name: monsterName, element: elementGetter, immune: immuneGetter, size: sizeGetter, twoStarWeakness: twoStarWeaknessGetter, threeStarWeakness: threeStarWeaknessGetter))

                        }
                }
            }
     }
}

This is my View:

import SwiftUI  

struct ContentView: View {  
    @EnvironmentObject var monsterT: MonsterC  

    var body: some View {  
        List(monsterT.monsters, id: \.self) { monster in  
            Text(monster.name)  
        }  
    }  
} 

And I did following to SceneDelegate.swift:

var window: UIWindow?  
var monsterT = MonsterC()  
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions {


    // Create the SwiftUI view that provides the window contents.  
    let contentView = ContentView().environmentObject(monsterT)  


    if let windowScene = scene as? UIWindowScene {  
        let window = UIWindow(windowScene: windowScene)  
        window.rootViewController = UIHostingController(rootView: contentView)  
        self.window = window  
        window.makeKeyAndVisible()  
    }  
}

So my Problem is the list is empty. I figured out in init of class MonsterC the line monsterNames.append("\document.documentID)") does not append anything to monsterNames. But print("document: \(document.documentID)") is printing all monsterNames.

My google Firestore structure looks like this:

    Collection -> Document -> Fields
-------------------------------------------
    Monster -> Anjanath -> immune: fire,
                           element: fire

etc. There's only one collection ("Monster").

Can anyone explain to a beginner why .append is not working here but print is doing everything right?

Upvotes: 0

Views: 253

Answers (1)

You need to understand calling asynchronous functions. My advice is to restructure your code, it is not a good idea to do these async calls in your init().

Your function "db.collection("Monster").getDocuments() { (querySnapshot, err) in ..." is asynchronous. You must either wait till it is finished to use the results, or do what you need inside the function. Note you also have another async function "db.collection("Monster").document(monsterName).getDocument {"

So .append is not working because the results of your function "db.collection("Monster").getDocuments() { (querySnapshot, err) in ..." are not available when you do the .append.

So if you must use this dodgy code, try this to fix your array problem:

class MonsterC: ObservableObject {

@Published var monsters = [MonsterObj]()

init() {
    let db = Firestore.firestore()

    db.collection("Monster").getDocuments() { (querySnapshot, err) in
        if let err = err {
            print(err)
        } else {
            var monsterNames: [String] = []
            for document in querySnapshot!.documents {
                monsterNames.append("\(document.documentID)")
                print("document: \(document.documentID)")

            }

            for monsterName in monsterNames {
                print(monsterName)
                db.collection("Monster").document(monsterName).getDocument { (document, error) in
                    if let document = document, document.exists {
                        let elementGetter = document.get("element") as! String
                        let immuneGetter = document.get("immune") as! String
                        let sizeGetter = document.get("size") as! String
                        let twoStarWeaknessGetter = document.get("2 star weakness") as! String
                        let threeStarWeaknessGetter = document.get("3 star weakness")as! String

                        self.monsters.append(MonsterObj(name: monsterName, element: elementGetter, immune: immuneGetter, size: sizeGetter, twoStarWeakness: twoStarWeaknessGetter, threeStarWeakness: threeStarWeaknessGetter))

                    }
                }
            }

        }
    }
}
}

Upvotes: 2

Related Questions