0

I'm calling a function, that function initializes a class, and in that class I have an initialize method that does a request to Firestore. I also have a variable in that class, in which once I'm done looping through the database, I change this self.variable to equal whatever I just created. For some reason, I am not able to get this to finish, and when I initialize my class object, the value of the variable never changes.

(Assume User class consists of just name and age)

This is where I call my function and initialize my object

fileprivate func fetch(){
    let homeObject = Home()
    print (homeObject.user.count)
    //count prints out as 0
    print (homeObject.test)
    //test prints out as 2 .. should be 3 but I'm not sure why?
}

This is the class for Home

class Home{
    var user: [User] = []
    var test = 1
    required init() {
        self.test = 2
        var userLoop = [User]()
        let db = Firestore.firestore()
        db.collection("users").getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                for document in querySnapshot!.documents {

                    print("\(document.documentID) => \(document.data())")

                    let name_data = document.data()["name"]! as! String
                    let age_data = document.data()["age"]! as! String

                    let userTemp = User(name: name_data , age: age_data)

                    self.test = 3
                    userLoop.append(userTemp)
                }
                self.user = userLoop

            }
        }
    }

For some reason, test is able to change to the number 2, but I believe this initializer method doesn't finish, as it is doesn't go to the number 3 (as you see above, later in the initializer function I change it to the number 3). I would really appreciate the help, been stuck on this for hours now!

zyne
  • 25
  • 4
  • What does "asynchronous" mean? – matt Mar 03 '19 at 02:56
  • Perhaps you could declare the thing you need to eventually want to initialize as an optional var. Then, in the completion block of a successful response, then you could initialize it? – Adrian Mar 03 '19 at 03:28
  • Firebase is asynchronous; therefore this line `print (homeObject.user.count)` executes before the firebase closure because code is faster than the internet. This question has been asked a lot of times before. [this answer](https://stackoverflow.com/questions/43027817/how-to-perform-an-action-only-after-data-are-downloaded-from-firebase/43029121#43029121) should add some clarity. Do a search here on `[firebase]firebase is asynchronous` and read through the Q & A's. – Jay Mar 03 '19 at 15:07
  • @Jay I see this solution, and it is kind of a different problem than mine. Yes I can do stuff within the required init method in my Home class, but I need to have this information AFTER home is created, during just during. Also it only works if I change the required init method to a function method and call it in my fetch function. – zyne Mar 03 '19 at 16:45
  • The issue is you are trying to call firebase like a function and that's not going to work. What needs to happen is to get the data from Firestore first, then init your Home var with that data. So call Firestore to get the needed documents, then init your var with them `let home = HomeClass(initWithDocuments: documents)` – Jay Mar 03 '19 at 16:57

1 Answers1

0

Initialize Home with a completion block like below:

class Home{
    var user: [User] = []
    required init(with block: (Home)->Void) {
        var userLoop = [User]()
        let db = Firestore.firestore()
        db.collection("users").getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                for document in querySnapshot!.documents {

                    print("\(document.documentID) => \(document.data())")

                    let name_data = document.data()["name"]! as! String
                    let age_data = document.data()["age"]! as! String

                    let userTemp = User(name: name_data , age: age_data)

                    userLoop.append(userTemp)
                }
                self.user = userLoop

            }
            block(self)
        }
    }
}

and call it as:

var home = Home { (home) in
    //set your properties if needed.
}
Torongo
  • 1,021
  • 1
  • 7
  • 14
  • Hmm, this doesn't really provide a solution. While the vars in home can be initialized within the `var home = Home {}` closure, any code after that will execute *before* home is initialized. Duplicate the issue by adding a var to the class, assigning a value within the `var home = Home {}` and then add a print statement to print the var following the closure. It prints an empty string as it executes before the code in the class closure which is still the same issue as in the question. Also, the `block(self)` throws an err *Closure use of non-escaping parameter 'block' may allow it to escape* – Jay Mar 03 '19 at 15:33