No errors but list is empty // Google Firestore

I'm new to SwiftUI and tried my first project on it.


So I want to make an iOS app which is showing some data from Firestore in a list.

The list is empty because MonsterT is empty. How can I call the init of Class MonsterC?

In my SceneDelegate I added following line in func scene and added this variable:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    var monsterT = MonsterC()
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionIf using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

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

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }...

My List

import SwiftUI

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

My MonsterC Class with MonsterObj


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 immune", groesse: "Test size", twoStarWeakness: "Test 2 Weakness", threeStarWeakness: "Test3 Weakness")
   
    #endif
   
}

class MonsterC: ObservableObject {
    var db = Firestore.firestore()
    @Published var monsters = [MonsterObj]()
   
    init() {
       
        var monsterCount: [String] = [""]
        monsterCount.remove(at: 0)
        db.collection("Monster").getDocuments() { (querySnapshot, err) in
            if let err = err {
                print(err)
            } else {
                for document in querySnapshot!.documents {
                    monsterCount.append(document.documentID)
                   
                }
            }
        }
           
            for monsterName in monsterCount {
                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.init(name: monsterName, element: elementGetter, immune: immuneGetter, size: sizeGetter, twoStarWeakness: twoStarWeaknessGetter, threeStarWeakness: threeStarWeaknessGetter))
                       
                        }
                }
            }
     }

What am I doing wrong or missing?

Thanks in advance 🙂

I would try to change your ContentView as this:



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

thanks I added it but it said MonsterObj must be Hashable.
So I added Hashable to MonsterObj

But the list is still empty

(updatet my code)

Did you check that monsterT.monsters is effectively non empty ?


struct ContentView: View {
    @EnvironmentObject var monsterT: MonsterC
   
    var body: some View {
       VStack {
             Text(monsterT.monsters.count)
             List(monsterT.monsters, id: \.self) { monster in
                 Text(monster.name)
             }
          }
    }
}

well it is empty.

How can I call the init of MonsterC?

Thought it would be called when I initialised var monsterT

So, let's first look at this init.


init() {
    var monsterCount: [String] = [""]
    monsterCount.remove(at: 0)
    db.collection("Monster").getDocuments() { (querySnapshot, err) in
        if let err = err {
            print(err)
        } else {
            for document in querySnapshot!.documents {
                monsterCount.append(document.documentID)
               
            }
        }
    }
   
    for monsterName in monsterCount {
        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.init(name: monsterName, element: elementGetter, immune: immuneGetter, size: sizeGetter, twoStarWeakness: twoStarWeaknessGetter, threeStarWeakness: threeStarWeaknessGetter))
               
            }
        }
    }
}


What do you do line 3 ? You could simply replace 2 and 3 by

var monsterCount: [String] = []


Add a print line 10

              print("document", document.documentID)


Add line 14:

                print("monsterCount", monsterCount)

Note: what a bizarre name here monsterCount. It is not a count, it is an array of names (something like monsterNames should be more appropriate


And line 24:

                print("document exists", elementGetter)


And tell exactly what you get on console when you run.

I noticed that this init is probably not called because I have print(monsterName) there but when I run the App it doesn‘t show any output. So I guess the problem is: init is not executed.

No, you print(monsterName) at line 41, inside the loop ! So if it is empty, you don't get there.


Do the change I proposed and tell what you get.

In your `init()` of `MonsterC`, the loop `for monsterName in monsterCount {...}` should be inside the completion handler of `getDocuments`.

Outside of the completion handler, your `monsterCount` is not filled yet.

(And `monsterCount` is not an appropriate name to store IDs.)


Try something like this:

class MonsterC: ObservableObject {
    var db = Firestore.firestore()
    @Published var monsters = [MonsterObj]()
    
    init() {
        db.collection("Monster").getDocuments() { (querySnapshot, err) in
            if let err = err {
                print(err)
            } else {
                var monsterIDs: [String] = []
                for document in querySnapshot!.documents {
                    monsterIDs.append(document.documentID)
                }
                for monsterName in monsterIDs {
                    print(monsterName)
                    self.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.init(name: monsterName, element: elementGetter, immune: immuneGetter, size: sizeGetter, twoStarWeakness: twoStarWeaknessGetter, threeStarWeakness: threeStarWeaknessGetter))
                        }
                    }
                }
            }
        }
    }
}

That's the common issue of asynchronism.


May look at this other thread for somehow similar situation and explanation.

https://forums.developer.apple.com/thread/132081

No errors but list is empty // Google Firestore
 
 
Q