0

I'm pretty new to SwiftUI and using Firebase and have stumbled upon retrieving a value from a document on Firebase. I've done some Googling but have yet to find a way to do this.

My code so far looks like this:

//Test1: get user info
func readUserInfo(_ uid: String) -> String {
    let db = Firestore.firestore()
    let docRef = db.collection("users").document(uid)
    var returnThis = ""
    docRef.getDocument { (document, error) -> String in
        if let document = document, document.exists {
            let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
            print("Document data: \(dataDescription)")
            returnThis = dataDescription
            return(dataDescription)
        } else {
            print("Document does not exist")
        }
    }
    return returnThis
}


//Test2: get user info
func readUserInfo(_ uid: String) -> String {
    let db = Firestore.firestore()
    let docRef = db.collection("users").document(uid)
    var property = "not found"
    docRef.getDocument { (document, error) in
        if let document = document, document.exists {
            property = document.get("phoneNumber") as! String
        }
    }
    return property
}

Test1: Got an error saying "Declared closure result 'String' is incompatible with contextual type 'Void'"

Test2: The returned value is always "not found", so I guess assigning document.get("phoneNumber") as! String to the property variable didn't work.

In both cases, I was able to print the value out. However, I want the value to be passed to a Text element so I can display it on the interface.

Could someone please help? Thanks so much in advance.

1 Answers1

0

The reason why the value is never found is because the network request you are making takes time to retrieve that data. By the time it reaches the return statement the data from firebase has not been been retrieved yet so it will just return the value it was initially set to which was an empty string. What you should do is add a completion handler to the function so that once the data is finally retrieved you can then return it.

struct ContentView: View {
    @State private var text: String = ""
    
    var body: some View {
        Text(text)
            .onAppear {
                readUserInfo("sydQu5cpteuhPEgmHepn") { text in
                    self.text = text
                }
            }
    }
    
    func readUserInfo(_ uid: String, _ completion: @escaping (String) -> Void) {
        let db = Firestore.firestore()
        let docRef = db.collection("users").document(uid)
        docRef.getDocument { document, error in
            if let document = document, document.exists {
                let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
                print("Document data: \(dataDescription)")
                completion(dataDescription)
            } else {
                completion("Document does not exist")
            }
        }
    }
}
  • Hi @Jack, thank you so much for your response! It worked!!! I'm curious though why I was able to print out dataDescription. If the data was retrieved when the code reaches the print statement, it surely would have been ready before it reached the return statement? – user13823002 Feb 07 '22 at 12:39
  • @user13823002 What you are describing is a synchronous function so the program would actually wait for the function to finish. In our case the getDocument function is asynchronous so it will run in the background and once done notify you. –  Feb 07 '22 at 13:37
  • @user13823002 the [getDocument](https://firebase.google.com/docs/reference/swift/firebasefirestore/api/reference/Classes/DocumentReference#getdocument) function actually has a completion handler as a parameter meaning that it is an asynchronous function. Basically these completion blocks allow the flow of the program to not be interrupted so in your test examples the readUserInfo function did not wait it for the getDocument function to finish. I suggest you read up on [closures](https://docs.swift.org/swift-book/LanguageGuide/Closures.html). –  Feb 07 '22 at 13:54