简体   繁体   中英

How to retrieve data from Firebase, set it to a global variable, then pass it to the next view controller?

First and for all, this question is regarding iOS, not android. Hopefully it won't get marked as duplicated to the android one again.

Secondly, to explain clearly, these are my intentions on how the app should run:

  1. Perform checks inside of the "shouldPerformSegueWithIdentifier"
  2. Check for empty username and password. If empty, return false and display alert

NOTE : I am able to do up till point 2. I know how to retrieve the data from firebase. The problem lies with point 3 onwards.

  1. Else, proceed on and compare data in the firebase
  2. If a match is found, get the documentID and set it to the global variable and return TRUE so that the prepareForSegue method is able to run.
  3. Else, return FALSE and display alert that no matching credentials

THE PROBLEM

I did ALOT of research for solution on stack overflow, and I understand its something related to the async thingly where the code OUTSIDE would run first. However, I can't find a single solution related with shouldPerformSegue method.

I tried out the completionHandler method though. However, that piece of code cannot return true or false inside that method, in return, unable to determine if the segue should be performed or not. Also, data can only be retrieved within that method when I need that userID to be passed to other view controllers.

Here is the shouldPerformSegueWithIdentifier code (Pardon me if the formatting is off because I don't normally ask question in stack overflow, but I can't help it because this is irritating me as I can't find a solution to it after a full day combing stack overflow. Do not mind the "return true" though, cause I still need to access other view controllers in the mean time, hence, apart from the empty checks, I set it to return true for now.):

override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
        let username : String = outUsername.text!
        let password : String = outPassword.text!
        if username.isEmpty && password.isEmpty {
            let alert = UIAlertController(title: "Error", message: "Username and/or password cannot be empty", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Close", style: .default, handler: nil))
            present(alert, animated: true, completion: nil)
            return false
        } else {
            db.collection("user").getDocuments() { (querySnapshot, err) in
                if err != nil {
                    print("Error getting documents")
                } else {
                    for document in querySnapshot!.documents {
                        let username02 : String = (document["username"] as? String)!
                        let password02 : String = (document["password"] as? String)!
                        if username02 == username && password02 == password {
                            self.userID = document.documentID
                        }
                    }
                }
            }
            return true
        }
    }

The problem in else check is that :

You perform an async task, so what happens when you go through else check is that you return true before the data is retrieved from firebase .

So what you need to do is to use completionHandler or some notification and change the function return type to void like this :


override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?, completionHandler:  @escaping ( _ shouldPerformSegue : Bool, _ userId : String) ->())  {
    let username : String = outUsername.text!
    let password : String = outPassword.text!
    if username.isEmpty && password.isEmpty {
        let alert = UIAlertController(title: "Error", message: "Username and/or password cannot be empty", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Close", style: .default, handler: nil))
        present(alert, animated: true, completion: nil)
        completionHandler(false,"")
    } else {
        db.collection("user").getDocuments() { (querySnapshot, err) in
            if err != nil {
                print("Error getting documents")
            } else {
                for document in querySnapshot!.documents {
                    let username02 : String = (document["username"] as? String)!
                    let password02 : String = (document["password"] as? String)!
                    if username02 == username && password02 == password {
                        completionHandler(true,document.documentID)

                    }else{ 
                        completionHandler(false,"")
                }
            }
        }
    }
}

I assumed after successful login you have received your data (self.userID = document.documentID). then you can use that data in many way...

Method 1: UserDefaults after successful login set your user id in to UserDefaults. like

UserDefaults.standard.set(true, forKey: "userLoggedIn")
UserDefaults.standard.set(documentID, forKey: "userId")
UserDefaults.standard.synchronize()

and your next homeViewController you can get back, like

UserDefaults.standard.bool(forKey: "userLoggedIn")  // true 
UserDefaults.standard.string(forKey: "userId")  //documentID

Method 2: Segue

set received value to global var of login ViewController and use it in performSegue.

call your Segue

self?.performSegue(withIdentifier: "loginToMain", sender: self)

auto perform method, set your global value to homeViewController variable. i assume HomeViewController have var userID..

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

                if (segue.identifier == "loginToMain") {
                    if let target = segue.destination as? homeViewController
                    {
                        target.fromLogin = true
                        target.userId = self.userId
                    }
                }
    }

Hope, it helps..

stackoverflow is always useful

So after trying for numerous days, finally able to get it working right, although loading is a bit slow, but for now that will do since it's just a sample prototype application. If any of you encounter empty population after retrieval from Firebase and need to pass that data to another controller, here is how I did it:

IBAction from button code:

@IBAction func actLogin(_ sender: UIButton) {
    let username : String = outUsername.text!
    let password : String = outPassword.text!
    if username.isEmpty && password.isEmpty {
        let alert = UIAlertController(title: "Error", message: "Username and/or password cannot be empty", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Close", style: .default, handler: nil))
        present(alert, animated: true, completion: nil)
    } else {
        var userID : String = ""
        var recordFound : Bool = false
        db.collection("user").getDocuments() { (querySnapshot, err) in
            if err != nil {
                print("Error getting documents")
            } else {
                for document in querySnapshot!.documents {
                    let username02 : String = (document["username"] as? String)!
                    let password02 : String = (document["password"] as? String)!
                    if username02 == username && password02 == password {
                        userID = document.documentID
                        recordFound = true
                    }
                }
                if recordFound == true {
                    self.performSegue(withIdentifier: "nextCtrl", sender: userID)
                } else {
                    let alert = UIAlertController(title: "Error", message: "Please enter a valid account credential.", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "Close", style: .default, handler: nil))
                    self.present(alert, animated: true, completion: nil)
                }
            }
        }
    }
}

Prepare for segue method:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let tabBarCtrller = segue.destination as? UITabBarController, let userID = sender as? String {
            // To retrieve from Firebase once user and password login is in place
            let userID : String = userID
            // To Book Now controller
            let navCtrl = tabBarCtrller.viewControllers![0] as! UINavigationController
            let bknctrl = navCtrl.topViewController as! BookNowScreen01
            // To By Date controller
            let navCtrl02 = tabBarCtrller.viewControllers![1] as! UINavigationController
            let bdctrl = navCtrl02.topViewController as! DateDurationScreen01
            // To By Room controller
            let navCtrl03 = tabBarCtrller.viewControllers![2] as! UINavigationController
            let brctrl = navCtrl03.topViewController as! RoomScreen01
            // To View Bookings controller
            let navCtrl04 = tabBarCtrller.viewControllers![3] as! UINavigationController
            let vbctrl = navCtrl04.topViewController as! ViewBookings

            bknctrl.userIDfromLogin = userID
            bdctrl.userIDfromLogin = userID
            brctrl.userIDfromLogin = userID
            vbctrl.userIDfromLogin = userID
        }

        outUsername.text = ""
        outPassword.text = ""
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM