0

I wrote this code to retrieve the current user name, it gets the name right but it keep looping, what can I do to solve it?

as you can see I put a print statement and its all loop. I hope my problem description is clear and get a solution.

func getChildName1 ()->String
{
    let db = Firestore.firestore()
    var childName : String = "nil"

    db.collection("Child").getDocuments { snapshot, error in
      
        if error == nil {
            if let snapshot = snapshot {
                print("in childName snapshot") //this keeps printing
                DispatchQueue.main.async {
                    print("in childName DispatchQueue.main.async") //this keeps printing
                    self.childList = snapshot.documents.map { d in
                        Child(
                            id: d.documentID,
                            email:d["Email"]as? String ?? "",
                            name: d["name"]as? String ?? ""
                        )
                    }
                }
            }
        }
        else {
            // Handle the error
        }
    }
      
    for item in childList
    {
        if item.id == String(Auth.auth().currentUser!.uid)
        {
            childName = item.name
            print(childName) //this keeps printing
        }
           
    }
     
    return childName
} // end getChildName
Leonardo
  • 2,439
  • 33
  • 17
  • 31
Squirrel
  • 1
  • 2
  • 2
    It is not clear where `getChildName1` is called, maybe it's called several times, maybe after a `didSet {}` or something. But it looks like there is a flaw in your code: if you need to return a `Child.name`, why do you download the whole list in async mode? I suggest you move the `db.collection("Child").getDocuments {}` method to were it belongs, not inside the reading of a single child. Remember. that, every time you get those documents, the `childList` variable will be updated, the same `childList` that your function iterates to get one single child name. Maybe that's why you have the loop? – HunterLion Feb 26 '22 at 07:57
  • 1
    thank you for your response, but how can i do your suggestion? – Squirrel Feb 26 '22 at 12:35
  • 2
    Firebase is Asynchronous; the code following the Firebase function closure like `for item in childList` will execute *before* the code in the closure. See [this answer](https://stackoverflow.com/questions/62199268/swift-firebase-storage-code-block-not-executing/62201584#62201584) and [here](https://stackoverflow.com/questions/43027817/how-to-perform-an-action-only-after-data-are-downloaded-from-firebase/43029121#43029121) and perhaps [this one](https://stackoverflow.com/questions/56025373/read-data-firebase-assign-value/56062986#56062986). Also this `DispatchQueue.main.async` is not needed. – Jay Feb 26 '22 at 14:38
  • thank you, can you see this please https://stackoverflow.com/questions/71296828/swiftui-problem-my-view-is-calling-a-function-many-times – Squirrel Feb 28 '22 at 15:00
  • That's the same question with the same issue. Please examine the links I included in my above comment. Also see [this](https://stackoverflow.com/questions/56442492/searchbar-problem-while-trying-to-search-firestore-and-reload-the-tableview/56446914#56446914) and see [this question](https://stackoverflow.com/questions/56296280/firebase-datadescription-returns-empty-array/56297185#56297185) because it's exactly the same issue and the solution applies. – Jay Feb 28 '22 at 21:44

1 Answers1

0

Firstly, your task is a bit confusing because the function is designed to get a single string (name) but you fetch an entire collection of documents which suggests there may be multiple names to deal with, so I've modified the function to be more idiomatic. Secondly, you cannot return something useful from an asynchronous function without making it an async function, which has limited support as of right now. Therefore, consider using a completion handler.

func getChildNames(completion: @escaping (_ name: String) -> Void) {
    Firestore.firestore().collection("Child").getDocuments { (snapshot, error) in
        guard let snapshot = snapshot else {
            if let error = error {
                print(error)
            }
        }
        for doc in snapshot.documents {
            if let email = doc.get("Email") as? String,
               let name = doc.get("name") as? String {
                let child = Child(id: doc.documentID,
                                  email: email,
                                  name: name)
                self.childList.append(child)
                completion(name)
            }
        }
    }
}

getChildNames { (name) in
    print(name)
}
trndjc
  • 11,654
  • 3
  • 38
  • 51
  • 1
    As mentioned, the OP is trying to get a single user `Name` from a document whose documentId is the current users uid. This code won't do that. Also, if there are 3000 documents in `Child` collection, it iterates over all 3000 of them for no reason (?) and calls the completion handler repeatedly. Wouldn't a better solution be: directly read the document they want and pass to the completion handler? `.collection("Child").document("uid").getDocument` ? Perhaps explaining your code a bit it would clarify. [getDocument](https://firebase.google.com/docs/firestore/query-data/get-data#get_a_document) – Jay Mar 03 '22 at 19:14