Firebase user can not be logged out - Swift - swift

I run the app in Xcode simulator and log in with user and password then I log out, the logOutOutlet and bookingsOutlet are hidden and logInOutlet button shows. So far it behaves as expected.
2
Next, stop the app from Xcode and run it again, but now it behaves as if I was already logged in: shows logOutOutlet,bookingsOutlet, hides logInOutlet. It behaves in the same when the app is suspended, terminated or deleted. ( i.e, Stop in Xcode or cmd + H * 2 and swipe up.)
Now, assuming that I have an active logIn session in the next ViewController I try to retrieve data from FireBase Realtime DataBase, but I get this error. Error Domain=com.firebase Code=1 "Permission Denied" UserInfo={NSLocalizedDescription=Permission Denied}. Why can I not log out completely?
#IBAction func logOut(sender: AnyObject) {
// if the current user is logged in, try to log out and if logout is successful:
// hide: logOut button & bookings button
// show: logIn button
if FIRAuth.auth() != nil {
do {
try FIRAuth.auth()?.signOut()
print("the user is logged out")
} catch let error as NSError {
print(error.localizedDescription)
print("the current user id is \(FIRAuth.auth()?.currentUser?.uid)")
}
self.logInOutlet.hidden = false
self.logOutOutlet.hidden = true
self.bookingsOutlet.hidden = true
} // end of if.. FIRAuth.auth()
}
override func viewDidLoad() {
super.viewDidLoad()
// if the user is logged out
// hide: logOut button & bookings button
// show: logIn button
// if FIRAuth.auth()?.currentUser?.uid == nil {
if FIRAuth.auth() == nil {
self.logInOutlet.hidden = false
self.logOutOutlet.hidden = true
self.bookingsOutlet.hidden = true
// it still prints the user id, why?
if let userNOTLogged = FIRAuth.auth()?.currentUser?.uid {
print("User should NOT be logged \(userNOTLogged)")
}
// if the user is logged in
// hide: logIn Button
// show: logOut button & bookings button
} else if
FIRAuth.auth() != nil {
self.logInOutlet.hidden = true
self.logOutOutlet.hidden = false
self.bookingsOutlet.hidden = false
}
}
// Log in code
FIRAuth.auth()?.signInWithEmail(email.text!, password: password.text!, completion: { (authData, error) in
let customError = error?.localizedDescription
if error != nil {
print(customError)
// display an alert with the error
self.displayAlert()
} else {
print("The user has been logged in")
//if signIn was successful, instantiate the view controller with identifier SWrevelViewidentifier
let toMenuOptions = self.storyboard?.instantiateViewControllerWithIdentifier("SWrevelViewidentifier")
self.presentViewController(toMenuOptions!, animated: true, completion: nil)
}
})
}

I had this exact same problem. Since this happens when you relaunch, try putting this code in your app.delegate.
FIRAuth.auth()?.addAuthStateDidChangeListener { auth, user in
if let user = user {
// User is signed in.
} else {
// No user is signed in.
}
}

Related

Firebase Login Persistence Swift

I'm using Firebase to handle my user register and login for my app. But if I log in, and then close my app entirely - the user is forced to re-log in. I'd like to keep the user logged in unless they click "Log out"
My login code is this:
Auth.auth().signIn(withEmail: email, password: password, completion: {(user, error) in
if let firebaseError = error {
print(firebaseError.localizedDescription)
return
}
self.presentTabBar()
})
}
}
How do I keep this user logged in unless specifically told to logout?
Here's a handy full example for 2020:
Anywhere in your iOS+Firebase app, you can simply say:
guard let uid = Auth.auth().currentUser?.uid else {
return print("no current user!")
}
Thus, on the launch screen of your app, simply:
import UIKit
import Firebase
import FirebaseUI
class ViewController: UIViewController, FUIAuthDelegate {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard let uid = Auth.auth().currentUser?.uid else {
return print("no current user, hence AuthUI flow...")
basicAuthUIFlow()
}
print("User .. \(Auth.auth().currentUser?.displayName)")
continueWhenUserPresent()
}
It's that easy. So then as usual ...
private func basicAuthUIFlow() {
let authUI = FUIAuth.defaultAuthUI()
authUI?.delegate = self
let pp: [FUIAuthProvider] = [ FUIGoogleAuth() ]
authUI?.providers = pp
if let authVC = authUI?.authViewController() {
present(authVC, animated: true)
}
}
func authUI(_ authUI: FUIAuth,
didSignInWith authDataResult: AuthDataResult?,url: URL?, error: Error?) {
let u = authDataResult?.user
print("Successful login via authUI \(String(describing: u?.displayName))")
continueWhenUserPresent()
}
private func continueWhenUserPresent() {
.. pushViewController .. your first screen
}
Check if the user is logged in or not:
if Auth.auth().currentUser != nil {
// User is signed in.
// ...
} else {
// No user is signed in.
// ...
}
if the user is logged in, then go the Home ViewController. This way when he opens the app again he will go to the Home ViewController unless he sign outsFIRAuth.auth().signOut()
For more info check this: https://firebase.google.com/docs/auth/ios/manage-users
For keep user login you need to check currentUser Auth session, If it's not nil then you can redirect user to Home screen.
Call "setRootViewController" method from didFinishLaunchingWithOptions, just after FirebaseApp.configure() code
Swift 4
func setRootViewController() {
if Auth.auth().currentUser != nil {
// Set Your home view controller Here as root View Controller
self.presentTabBar()
} else {
// Set you login view controller here as root view controller
}
}

sign out for Facebook and Google

I am trying to implement a sign out button that depending on the current user type (Facebook vs Google) it will sign out based on which type of current user is logged in. I am able to login with different accounts but want the sign out function to be conditional based on what type of account is logged in...thanks in advance!
// sign out functions
func handleSignOut() {
// facebook sign out
UserDefaults.standard.setIsLoggedIn(value: false)
FBSDKLoginManager().logOut()
print("did log out of facebook...")
// google signout
let firebaseAuth = FIRAuth.auth()
do {
try firebaseAuth?.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
let loginController = LoginController()
present(loginController, animated: true, completion: nil)
}
func loginButtonDidLogOut(_ loginButton: FBSDKLoginButton!) {
do {
try FIRAuth.auth()!.signOut()
} catch let logoutError {
print(logoutError)
}
}
Try storing in the UserDefaults which account your user is signed in as (e.g. store a "account" key with a string value "google" or "facebook").
Then, in your sign-out method, handleSignOut() {...}, test for that value, i.e.
let at = (UserDefaults.getAccountType())
if at == "google" {
handleGoogle()
} else if at == "facebook" {
handleFacebook()
}
*Implement your own accessor for UserDefaults.getAccountType

Show View Controller based on the users Firebase login state

In my app I want do display different ViewControllers depending on if the user is already logged in or not. Like if the user is logged in present VC_A if not, present VC_B. I tried the following code in my vc_login:
if let user = FIRAuth.auth()?.currentUser {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "vc_home") as! ViewController_Home
self.present(vc, animated: true, completion: nil)
} else {
// Do Nothing
}
You can do it like that (hints are in the code comments):
if FIRAuth.auth()?.currentUser?.uid == nil {
// user is not logged in
// present VC_B
} else {
// user is logged in
// present VC_A
}
Or you can use the ternary conditional operator like this:
FIRAuth.auth()?.currentUser?.uid == nil ? presentViewControllerB() : presentViewControllerA()
func presentViewControllerA() {
// call if logged in
}
func presentViewControllerB() {
// call if not logged in
}

Logout on Facebook's Firebase

This question is asked before:
Firebase sign out not working in Swift
Logging a user out with Firebase 3 and Swift still shows the `currentUser`
Firebase - iOS Swift: FIRAuth.auth().signOut() not signing out current user
However all the answers are not working for me. I have VC 1 where the user can login, and in the viewdidappear I have a print that prints the user?.uid. In VC 2 I have a button that should logout the user and goes back to VC 1. The code in VC 2:
#IBAction func logOut(_ sender: UIButton) {
if FIRAuth.auth()?.currentUser != nil {
do {
try! FIRAuth.auth()!.signOut()
} catch let error as NSError {
print(error.localizedDescription)
}
}
FBSDKAccessToken.setCurrent(nil)
loggedIn = false
storedValuesData.setValue(nil, forKey: "savedLoginEmail")
storedValuesData.setValue(nil, forKey: "savedLoginPassword")
jumpToVC1()
}
When I am back at VC 1, when the user pressed the logout button, the print prints again the user's UID. But that user should be logged out, therefore the print should be nil. How can I make sure VC 1 only gets presented when I am sure the user is logged out? I thought completion blocks would be nice, but I am not sure how that would work here...
You don't need completion blocks, as the method is synchronous.
#IBAction func logOut(_ sender: UIButton) {
guard FIRAuth.auth()?.currentUser != nil else {
return
}
do {
try FIRAuth.auth()?.signOut()
FBSDKAccessToken.setCurrent(nil)
loggedIn = false
storedValuesData.setValue(nil, forKey: "savedLoginEmail")
storedValuesData.setValue(nil, forKey: "savedLoginPassword")
jumpToVC1()
} catch let error as NSError {
print(error.localizedDescription)
}
}
AccessToken.current=nil is worked.Because logout is not reverse it.
do {
try Auth.auth().signOut()
AccessToken.current=nil
return true
} catch {
return false
}

Swift Parse not displaying error message on signup

I've created a user class which has a Parse PFUser() object in it and a string for error_messages.
In the user class is a signup function which will use the PFUser object and perform signUpInBackgroundWithBlock().
In that function it should set a flag notifying the main view controller that an error occurred if one does as well as set the error_message string in the User object with the error message passed back from PFUser.
However what happens is the function doesn't finish executing once an error occurs for example if an incorrect email format is entered such as aaaa.com instead of aaa#a.com the function won't return and set the flag instead the error message passed from PFUser is just displayed in the console window.
I've spent a few days now trying everything imaginable to set the flag and the error message in the user class but I can't figure it out.
Here is the class code
class User {
var user_db_obj = PFUser()
var error_message = "Please try again later"
//var user_name: String
//var pass_word: String
//Constructor for User object
init(user: String, pass: String){
user_db_obj.username = user
user_db_obj.email = user
user_db_obj.password = pass
}
//This function signs a user up to the database
func signUp() -> Bool {
var error_in_sign_up: Bool = true
user_db_obj.signUpInBackgroundWithBlock {(succeeded: Bool?, error: NSError?) -> Void in
//stop spinner and allow interaction events from user
activityIndicator.stopAnimating()
UIApplication.sharedApplication().endIgnoringInteractionEvents()
if error == nil{
error_in_sign_up = false
//sign up successful
}
else{
let error_string = error!.userInfo["error"] as? String
self.error_message = error_string!
}
}
if error_in_sign_up == true{
return false
}
else{
return true
}
}
Here is the view controller code that calls the signup function from User class.
//Action for signup button
#available(iOS 8.0, *)
#IBAction func signup_btn(sender: AnyObject) {
if email_tf.text!.isEmpty || pass_tf.text!.isEmpty {
//if email or password field blank display error message
displayAlert("Error in form", msg: "Please enter a username and password")
}
else{ //perform actual signup/login if email and password supplied
//Display spinner while database interaction occuring and ignore user interactions as well
activityIndicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 50, 50))
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
let theUser = User(user: email_tf.text!, pass: pass_tf.text!)
//sign up
if signup_mode == true{
if theUser.signUp() == false{
displayAlert("Failed SignUp", msg: theUser.error_message)
}
}
//login
else{
if theUser.login() == false{
displayAlert("Failed Login", msg: theUser.error_message)
}
}
}
}
the problem is that function signUpInBackgroundWithBlock doesnt run on mainthread, if you want to keep this functions you would have to register notification and then listen when it is successful or not in the other viewController... something like this