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:
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.
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.