0

i'm creating my first app (and newbie in swift). When i login from Facebook, the name and email are saved in Firestore. I'm trying to set the name from facebook to a variable to use it in other places, but i can't assign the value, always shows "nil" in the console. Anyone can help me please?

  1. I set the variable
var userN: String?
  1. I get the data from Firestore
func readDatabase(){

    let db = Firestore.firestore()
    let docRef = db.collection("users").document("email")


    docRef.getDocument { (document, error) in
        if let document = document, document.exists {
            let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
            print("Document data: \(dataDescription)")

            let data = document.data()

            let userName = data!["name"]! as! String
            print(userName)

            let userEmail = data!["email"]! as! String
            print(userEmail)

let containerController = ContainerController()
            let containerController.userN = userName;
            return
        }
    }

}

i want to assign userN = userName, to use it in other view How can i do that? thanks

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
jhonhlc
  • 1
  • 1
  • 1
    Welcome to SO, afew things. First, store additional user info in Firestore in a /users collection, but use the users uid as the documentId, not the email. It's often best practice to disassociate documentId's from the data they contain, especially if the id you want to use is dynamic (may change) like an email address; uid's are static. Next is that [Firestore is asynchronous](https://medium.com/google-developers/why-are-firebase-apis-asynchronous-callbacks-promises-tasks-e037a6654a93) so read up on that; you should not be calling it synchronously and attempting to return a value. – Jay Jul 07 '19 at 13:22
  • Also, document.data().map(String.init(describing:)) ?? "nil" is not needed to read a field in Firestore. That's how its shown in the documentation but I think your using it out of context (and that documentatation is not that clear IMO). If you can include a description of your Firestore structure, I'll show you how to do that. – Jay Jul 07 '19 at 13:38
  • Hi, thanks for you advise. Well i'm using the email because i'm testing the firebase conection but in a future i will use the uid as you said. And i'm using document.data() because the firebase helper says that, i'm new using that. The structure of my firestore has a "users" collection and inside it, i have the fields device, username, email and photo url, retrieved from google or facebook login. I just want to set this fields in variables, for use it in my menu view, and show the info of the user logged in. – jhonhlc Jul 07 '19 at 19:34

2 Answers2

0

If you are using StoryBoards you can pass this through the segue function;

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "YourStoryBoardSegue" {
            if let viewController = segue.destination as? ContainerController {
                viewController.userN = userName
            }    
        }
   }

otherwise, best practice would be to use a delegate method.

Search stack overflow for best practices using delegates to pass data.

David Henry
  • 1,972
  • 20
  • 43
  • I'm not using storyboards. Can you give me an example using delegates? – jhonhlc Jul 07 '19 at 13:22
  • @jhonhlc While this is a nifty bit of code, it has nothing to do with your question. Your userName will continue to be nil because userName is not valid outside of the closure. You need to resolve that first before trying to pass anything to another controller. – Jay Jul 07 '19 at 13:24
  • @jhonhlc And while delegate patterns are a powerful tool, that's not needed in this case and it's definitely not 'best practice' just to *use it in other places* as your question states. In fact, unless you are passing data back to a viewController, it's not needed at all. Because you're using Firebase which is live-updating, you may find you rarely pass data back. Read [this question](https://stackoverflow.com/questions/5210535/passing-data-between-view-controllers/9736559#9736559) and great answers for more info. – Jay Jul 07 '19 at 13:36
  • Hi @jay , as i told in the question, i'm new here, and i don't know how to do that. i want to pass information from the login view to my menu view and show the information of the user logged in. What i have to change to my code to improve it, and declarate a var with data from firestore? – jhonhlc Jul 07 '19 at 19:37
0

The question is extremely broad and without knowing details the only way to address it is with a general answer that specifically addresses how to read data from Firestore and save it in a variable to be used later.

Suppose your Firestore looks like this

root
   users
      uid_0
         name: "users name"
      uid_1
         name: "another users name"

and when the app loads, we want to read the users name and store it in in a variable per your question:

a variable to use it in other places

Here's what that could look like

class ViewController: NSViewController {

    var usersName = ""

    override func viewDidLoad() {
        super.viewDidLoad()
        FirebaseApp.configure()
        self.db = Firestore.firestore()
        let settings = self.db.settings
        self.db.settings = settings
        self.readUserName()
    }

    func readUsersName() {
        let users = self.db.collection("users")
        let thisUser = users.document("uid_1")
        thisUser.getDocument(completion: { documentSnapshot, error in
            if let error = error {
                print(error.localizedDescription)
                return
            }
            let name = documentSnapshot?.get("name") as! String
            self.usersName = name
        })
    }

The code sets up Firestore, reads the user name from the uid_1 document and stores it in a variable where it could be used later.

Suppose we want to let the user change their name. There's 100 ways to do it; passing data via a segue, use a delegate method or open a detail view controller and before it closes, have this master controller read the updated name from a textField and save the data. You could even pass the users uid and then in the detail viewcontroller read the document via that uid and then update it upon closing. However, all of those go beyond the scope of the question.

Jay
  • 34,438
  • 18
  • 52
  • 81