1

i'm new to coding in Swift, I have been looking around for some time for a solution to this issue and as of yet unable to find anything that solves my problem.

I am using a function to return some data from Firebase. After many errors and much research, I have managed to get the function 'working' without throwing errors, but the data that is returned is blank. Essentially I am trying to return multiple values and hold them in an array when I call the function.

The data is returning fine from Firebase, when I print out the variable just after setting it, it will print out correctly but when I do the same just before returning the function, it returns blank. If I try to return the function just after setting the data, I get the error "Unexpected non-void return value in void function".

Here is the full code:

func loadClientData(client_id: String) -> (address_1: String, address_2: String, city: String, company_name: String, contact_name: String, county: String, email: String, phone: String, postcode: String, country: String) {
        let db = Firestore.firestore()
        let userID : String = (Auth.auth().currentUser!.uid)

        print("\(userID)/clients/existing/\(client_id)")

        let docRef = db.collection("\(userID)/clients/existing/").document(client_id)

        var address_1 = ""
        var address_2 = ""
        var city = ""
        var company_name = ""
        var contact_name = ""
        var county = ""
        var email = ""
        var phone = ""
        var postcode = ""
        var country = ""

        docRef.getDocument { (document, error) in
            if let document = document, document.exists {

                let data = document.data()

                address_1 = data?["address_1"] as? String ?? ""
                address_2 = data?["address_2"] as? String ?? ""
                city = data?["city"] as? String ?? ""
                company_name = data?["company_name"] as? String ?? ""
                contact_name = data?["contact_name"] as? String ?? ""
                county = data?["county"] as? String ?? ""
                email = data?["email"] as? String ?? ""
                phone = data?["phone"] as? String ?? ""
                postcode = data?["postcode"] as? String ?? ""
                country = data?["country"] as? String ?? ""

                print("Company name is \(company_name)") // <---- THIS prints out the company name

            } else {
                print("client does not exist")
                return
            }
        }


        print("Company name is \(company_name)") // <---- THIS prints the company name as blank

        return (address_1: address_1, address_2: address_2, city: city, company_name: company_name, contact_name: contact_name, county: county, email: email, phone: phone, postcode: postcode, country: country)
    }

This is being called like so:

let companyInfo = loadClientData(client_id: self.items[indexPath.item].company)

        print(companyInfo)

And prints out the following:

(address_1: "", address_2: "", city: "", company_name: "", contact_name: "", county: "", email: "", phone: "", postcode: "", country: "")

Thanks in advance for your input.

2
  • 1
    Read up on the concept of "asynchronous". Commented Sep 9, 2019 at 19:04
  • Thanks for your input @rmaddy though i'm not sure how this is relevant here? As you can probably tell, I am very new to Swift :) Commented Sep 9, 2019 at 19:07

2 Answers 2

5

There are many questions on this site that are fundamentally the same as yours, and you should take a look at them. To avoid redundancy, I'll explain in simple terms, but I urge you to read up on similar questions.

In a nutshell, what's happening is that loadClientData fetches data asynchronously, meaning that it happens in the background while other stuff happens in the foreground. The foreground stuff includes your code that calls loadClientData and prints it out. The foreground stuff doesn't wait for the background stuff to finish.

You're getting nothing because the background stuff isn't finished processing by the time you print(companyInfo).

Real world example:

Your mom asks you to go buy a lemon for a dish she is cooking for dinner.

She starts cooking, but she has no lemon!

Why? Because you haven't yet returned from the supermarket, and your mom didn't wait.

If you want to make the foreground stuff wait, you have a lot of options, which you can consider by looking at other similar questions.

Sign up to request clarification or add additional context in comments.

6 Comments

Analogy: You're making dinner, and you give your kid money to go to the store to buy flour. You don't hand your kid the money with one hand and expect him to hand you the flour from his other hand right then and there. You give your kid the money, go back to your cooking, and wait for him to asynchronously go to the store and come back once he's done.
Thanks for that Daniel, looking through the posts on that link currently though I am unable to find anything there as yet that I can directly relate to my function. Ultimately, I am just looking for the best way to return this data. I understand the concept you're describing though I don't understand how to apply this to fix the issue. Thanks again for your input.
@DuncanC, my analogy is for the younger coders and yours for the older :)
Oh man, I missed your example. Did you add that to your post?
("Your mom asks you to go to the store to buy limes. She takes a shot of tequila and is annoyed that she doesn't have a lime to chase it with. Why? because you haven't returned with her limes yet!")
|
1

You need to refactor your function to not return anything, and take a completion handler. The completion handler is a block of code that the caller passes to your function. Your function will call the comletion handler once the results are available. This is a common coding pattern in Swift, and your firebase getDocument() call is written to use a completion handler.

Search on Swift completion handler design pattern to learn more.

**Note:

Using a completion handler is only one way to do this. You could also use the delegate design pattern, but completion handers are a more modern way of handling async events, and the way I would suggest handling your problem.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.