UserDefaults Auto-Login isn't Working - ios

So when the user first opens my app they will see a view with a button to proceed with logging in or registering. In my Login view, I have a button handler (attached via action) which is called when a user presses a login button. Despite me using UserDefaults to store logged in state, my app does not go straight to my Dashboard view automatically. Any ideas why?
// Firebase auth in doLogin action (Login VC)
Auth.auth().signIn(withEmail: un, password: pw)
{
(resp, err) in if (err == nil && resp != nil)
{
if (resp!.user.isEmailVerified)
{
// segue from Login VC to Dashboard VC will be exectuted
self.performSegue(withIdentifier: "loggedin", sender: nil)
// store the logged in state as true for the future
UserDefaults.standard.set(true, forKey: "loggedin")
UserDefaults.standard.synchronize()
}
}
}
Here is the code for the Main view controller to determine if it should proceed directly to dashboard.
func loggedIn() -> Bool {
// determine if the user is still logged into the app
return UserDefaults.standard.bool(forKey: "loggedin")
}
override func viewDidLoad() {
super.viewDidLoad()
if (loggedIn())
{
// logged in, return to the dashboard view controller
performSegue(withIdentifier: "persisted", sender: nil)
}
}
I've tried using print statements and either the loggedIn function isn't being executed for whatever reason or the console is just too flooded with Firebase debug log statements to even see these.

You have to embed your main view controller inside a UINavigationController to make the persisted segue work.

Related

Save Logged in state for appropriate user in IOS and Firebase

I'm using only one login screen for different users on the app I'm developing and I added a code to save the logged in user so that the user doesn't have to log in again next time they open the app. But the fact that I have different users, I'm only able to send the user to one segue.
I've tried adding the code to keep the user logged in, but if I login with another user it sends the user to the same view controller as before. Here's what I tried:
override func viewDidAppear(_ animated: Bool) {
if Auth.auth().currentUser != nil {
// User is signed in.
performSegue(withIdentifier: "studentSegue", sender: self)
} else {
// No user is signed in.
return
}
}
I'm trying to keep the user logged in for different users, but I'm not sure what code to use. To differentiate the users on the login page I used switch case to find the user "Type" and log in on that. But I want to keep the appropriate user logged in. Any help would be much appreciated
EDIT: What am trying to say is if two people on different phones try to login lets say student (phone A) and teacher (phone B) they should just log in once and not have to log in again when they close the app. But my login "PerformSegue" only lets me show the "studentSegue"
Let me restate the question
On my app, I have two different types of users, student and teacher. When a student logs in or restarts the app, it should take them to the student section via a segue. When a teacher logs in or restarts the app, it should take them to a teacher section via a segue.
A few things
Firebase doesn't have user types, only users - to Firebase, every user is the same as every other user. Your app code and structure determine the difference.
One solution is to ask Firebase what kind of user it is upon app start. here's some generic code:
override func viewDidAppear(_ animated: Bool) {
if Auth.auth().currentUser != nil {
let uid = currentUser.uid
let thisUserRef = fbRoot.child("users").child(uid)
thisUserRef.observeSingleEvent(..... {
let userType = //get userType from snapshot
if userType == "student" {
performSegue(withIdentifier: "studentSegue", sender: self)
} else {
performSegue(withIdentifier: "teacherSegue", sender: self)
}
}
} else {
// No user is signed in.
return
}
}
Another option is to store the userType in the userDefaults. So if the user is auth'd, get the user type from the defaults and perform the appropriate segue. I am not a huge fan of this approach due to security but it can work.
After trying quite a lot i managed to find the answer myself. So what i did was set an integer with UserDefaults.standard.set(1, forKey: "isLoggedIn") each time a user would log in. I set number 1 for student and number 2 for teacher and in viewDidAppear i did this:
override func viewDidAppear(_ animated: Bool) {
if UserDefaults.standard.float(forKey: "isLoggedIn") == 1 {
self.performSegue(withIdentifier: "studentSegue", sender: self)
}
if UserDefaults.standard.float(forKey: "isLoggedIn") == 2 {
self.performSegue(withIdentifier: "lecturerSegue", sender: self)
}
else{
return
}
}

Segue Still Goes Through Despite Conditional Statement

I'm new to swift and Xcode. I creating a user registration view controller and I'm using Firebase as my DB. Here is what I've written:
#IBAction func registerButton(_ sender: Any) {
//Register user functions
Auth.auth().createUser(withEmail: emailField.text! , password: passwordField.text!) { (user, error) in
if error != nil {
print (error!)
} else {
//success
print ("Registration successful")
self.performSegue(withIdentifier: "registerToList", sender: self)
}
}
}
When a user inputs data that is incorrectly formatted, such as blank fields or improperly formatted email, my console prints out the error. However, the user is still able to bypass the registration view controller and move on to the next screen. Why is that?
I also want to note that I'm using a Show segue - is this the issue? I can't use a push segue since it has been deprecated.

Swift Firebase login

I'm having a problem where every time I enter the right credentials, it brings me to one view controller then opens up the same view controller again even though I only have the login viewer controller linked to one view controller. If I don't enter the right credentials it still brings me into the linked view controller. Here is the code.
EDIT: Using a push segue(show)
#IBAction func loginTapped(_ sender: Any) {
if let Email = userEmail.text, let Pass = userPassword.text{
Auth.auth().signIn(withEmail: Email, password: Pass, completion: { (user, error) in
if error != nil{
print("incorrect")
}
else{
if error == nil{
self.performSegue(withIdentifier: "loginPage", sender: self)
print("correct")
}
}
})
}
}
I don't know if you've fixed your problem, but check your storyboard. Sounds like you have a segue connected from the button to the next ViewController which would result in pressing the button and it'll always push that ViewController.
To do this easily just see if you have a segue connected from the button to your destination ViewController in your MainStoryboard.

Accessing UserDefaults in Swift from other viewControllers

In my application, I use UserDefaults to store the user's login status (whether they are logged in or not), and their username. It works fine in that when I login, close the app, and open it again my app skips the login page and recognizes that I am already logged in. Although, I am now trying to install a logout button to a separate viewController. When clicked, this logout button needs to 1.) Reset UserDefaults.loginStatus to "False" 2.) Reset UserDefaults.username to nil 3.) Perform a segue to the login page.
Here is the related code from my ViewController.swift file. This is the first viewController which controls the loginPage.
import UIKit
import Firebase
let defaults = UserDefaults.standard
class ViewController: UIViewController {
func DoLogin(username: String, password: String) {
//I Am not including a lot of the other stuff that takes place in this function, only the part that involves the defaults global variable
defaults.setValue(username, forKey: "username")
defaults.setValue("true", forKey: "loginStatus")
defaults.synchronize()
self.performSegue(withIdentifier: "loginToMain", sender: self) //This takes them to the main page of the app
}
override func viewDidLoad() {
super.viewDidLoad()
if let stringOne = defaults.string(forKey: "loginStatus") {
if stringOne == "true" { //If the user is logged in, proceed to main screen
DispatchQueue.main.async
{
self.performSegue(withIdentifier: "loginToMain", sender: self)
}
}
}
}
Below is my code in SecondViewController.swift, particularly the logout function.
import UIKit
import Firebase
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let username = defaults.string(forKey: "username") {
checkAppSetup(username: username) //This is an unrelated function
//I included this because this works fine. Proving that I am able to read the defaults variable fine from this other viewController
}
}
#IBAction func logout(_ sender: Any) {
defaults.setValue("false", forKey: "username")
defaults.setValue("false", forKey: "loginStatus")
defaults.synchronize()
performSegue(withIdentifier: "logoutSegue", sender: nil)
}
When the logout function is run, the segue performs fine but the default values do not change. Can someone explain why and what I can do to get around this?
**Side note, I am not actually going to set the defaults to "false" and "false". That is just temporary for while I am debugging this issue.
Several things.
You should be using set(_:forKey:) and object(_:forKey) to read and write key/value pairs to defaults, not setValue(_:forKey). (Your use of defaults.string(forKey: "loginStatus") is correct, however.)
You should probably be writing a nil to the userName key:
defaults.set(nil, forKey: "username")
And your logout IBAction should almost certainly be setting loginStatus to false, not true.
Try changing those things.
Also, there is no reason to call synchronize unless you are terminating your app in Xcode rather than pressing the home button on the device/simulator in order to let it exit normally.
Hey i used the exactly same concept recently :
1) In your initial view, in the viewDidLoad() , check whether somebody is already logged in or not, and only one user can be logged in one device at a time, so we check like
let defaults = UserDefaults.standard
if defaults.object(forKey: "userName") != nil && defaults.object(forKey: "userPassword") != nil
{
let loginObject = self.storyboard?.instantiateViewController(withIdentifier: "YourSecondViewController") as! YourSecondViewController
//As someone's details are already saved so we auto-login and move to second view
}}
2) In your sign in button function , check whatever condition you want to check and then, inside the same, if condition satisfies then save data to userDefaults.
// If no details are saved in defaults, then control will come to this part, where we will save the entered userName and Password
let defaults = UserDefaults.standard
defaults.set(self.enteredUseName, forKey: "userName")
defaults.set(self.enteredPassword, forKey: "Password")
defaults.synchronize()
3) On logout button , delete the userDefaults and load the login view again :
let defaults = UserDefaults.standard
defaults.removeObject(forKey: "userName") //We Will delete the userDefaults
defaults.removeObject(forKey: "userPassword")
defaults.synchronize() //Sync. the defaults.
navigationController?.popToRootViewController(animated: true) //Move Back to initial view.
4) If you are using a navigation control, that you must be using :P then you will surely see the back button which will open the second view if clicked, for that you can hide the navigation bar in viewDidLoad() of your login view
self.navigationController?.navigationBar.isHidden = true

Facebook Sign In Segue Loads and Then Returns to Sign In Screen

So I have created a Sign In / Sign Up function using Parse and Swift.
After successful Facebook login the view controller I want to see pops up briefly but then the SignIn view controller comes back onto the screen. I want the user to be logged in and have access to the App. Why is this?
Below is my code for both view controllers. I have a storyboard with segues setup as shown below.
fbSignIn - Facebook Sign In Segue
goSignIn - Sign In Page Segue
Main View Controller (The View Controller I want to go to after Facebook Login)
class MainViewController: UIViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
if PFUser.currentUser() != nil {
} else {
// take user to SignInViewController through a custom segue
self.performSegueWithIdentifier("goSignIn", sender: self)
}
}
#IBAction func logOutUser(sender: UIButton) {
PFUser.logOut()
self.performSegueWithIdentifier("goSignIn", sender: self)
}
Sign In View Controller (Sign In and Facebook Sign In Button)
#IBAction func didTapFacebookConnect(sender: AnyObject) {
let permissions = [ "public_profile", "email", "user_friends" ]
PFFacebookUtils.logInInBackgroundWithReadPermissions(permissions) {
(user: PFUser?, error: NSError?) -> Void in
if let user = user {
if user.isNew {
print ("User signed up and logged in through Facebook!")
self.performSegueWithIdentifier("fbSignIn", sender: self)
} else {
print ("User logged in through Facebook!")
self.performSegueWithIdentifier("fbSignIn", sender: self)
}
} else {
print ("Uh oh. The user cancelled the Facebook login.")
self.performSegueWithIdentifier("fbSignIn", sender: self)
}
You Segue to your Facebook login page on a viewDidAppear method in your main viewcontroller. The behaviour you describe suggests that PFUser.currentUser() is nil so self.performSegueWithIdentifier("goSignIn", sender: self) is executed in viewDidAppear when your main viewcontroller appears and you immediately end up back in the sign in page.

Resources