How can I handle offline logout with Firebase in Swift? - ios

I'm unsure how to handle logout in my IOS app when the user is offline.
I'm using Firebase in Swift for authentication and it works perfectly when online for login/logout. However, I want to let the user logout when offline. Currently, when offline and the user clicks the Logout button, nothing happens on screen. If I restart the app, I am taken to the login screen as-if the logout occurred.
do {
try Auth.auth().signOut()
} catch let signOutError as NSError {
print (signOutError)
}
self.performSegue(withIdentifier: "unwindToMain", sender: self)
This code works for signout when online, but when offline the segue is not performed. I've also tried putting segue in the do and catch.

Please try the below code
if Auth.auth().currentUser != nil {
do {
try Auth.auth().signOut()
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Welcome")
present(vc, animated: true, completion: nil)
} catch let error as NSError {
print(error.localizedDescription)
}
}

Related

threading error for performSegue with viewController? iOS

I am trying to jump to a second view controller after I have authorized a user's TouchID. I am able to validate that the TouchID is working but I am having an issue of jumping to a second viewController.
I have created a SecondViewController and a Segue with the Identifier "dispenseScreen". However, whenever I try to jump to the second screen my program crashes.
#IBAction func touchID(_ sender: Any)
{
let context:LAContext = LAContext()
//Removes Enter Password during failed TouchID
context.localizedFallbackTitle = ""
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
{
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "We require your TouchID", reply: { (wasCorrect, error) in
self.isBiometryReady()
if wasCorrect {
self.performSegue(withIdentifier: "dispenseScreen", sender: self)
print("Correct")
}
else {
print("Incorrect")
}
})
} else {
//Enter phone password if too many login attempts
//Add message alerting user that TouchID is not enabled
}
}
There are no semantic errors in my code but I am receiving a threading error when I try to go to the second view controller.
You're trying to do the segue in the callback from evaluatePolicy. For anything involving UI, you need to make sure you're on the main thread: (wasCorrect, error) in DispatchQueue.main.async { ... }

Mapview delegate function continues to run after dismissing View Controller that contains mapview

I'm using mapbox and firebase.
I have a delegate function that updates the user's coordinates(inside of the firebase database) when the user's location changes.
To the best of my knowledge, it functions as it should when signed into the app. The mapviews delegate is the view controller (self.mapView.delegate = self)
func mapView(_ mapView: MGLMapView, regionDidChangeAnimated animated: Bool){
let latitude = mapView.userLocation?.coordinate.latitude
let longitude = mapView.userLocation?.coordinate.longitude
Database.database().reference().child(pathToCoord).updateChildValues(["latitude":latitude, "longitude":longitude], withCompletionBlock: { (err, ref) in
if err != nil { print(err!); return }
}
When I sign out of the app, I would like to stop updating the user location.
Ideally I would just like the View Controller with the map to go away completely and for everything on it to stop running.
I've written this sign out function that try several different methods of making sure that the location is no longer updated.
func signOut(){
for id in Auth.auth().currentUser?.providerData{
if id.providerID == "facebook.com"{
FBSDKLoginManager().logOut()
}
}
do {
try Auth.auth().signOut()
}catch let logoutError {
print(logoutError)
}
self.mapView.delegate = nil
if let vc = self.storyboard!.instantiateViewController(withIdentifier: "SignInViewController") as? SignInViewController{
UIApplication.shared.keyWindow?.rootViewController = vc
self.dismiss(animated: true, completion: nil)
}
}
Sometimes when I'm logged out though, I continuously get the error below in my console. The most logical solution I can think of for why this is happening is that the View Controller is still running. I don't know how to make it stop.
[Firebase/Database] updateChildValues: at `path/to/coord` failed: permission_denied
Error Domain=com.firebase Code=1 "Permission denied" UserInfo={NSLocalizedDescription=Permission denied}
Edit
So it looks like the problem was probably that I had this in my SignInViewController
if let uid = Auth.auth().currentUser?.uid{
if let vc = self.storyboard!.instantiateViewController(withIdentifier: "MainViewController") as? MainViewController{
vc.uid = uid
UIApplication.shared.keyWindow?.rootViewController = vc
}
}
And then the delegate would run once for each uid, as if two of the view controllers were running at the same time. When I signed out, I'm guessing the other one didn't sign out and kept running for the other user id.
This is off topic to my original question but I'd like to know what the proper way to check if a user is already signed in, and then sign them in is. Because clearly my method didn't work.
Why don't you try dismiss with Completion Handler block like below.
self.dismiss(animated: true, completion: {
if let vc = self.storyboard!.instantiateViewController(withIdentifier: "SignInViewController") as? SignInViewController{
UIApplication.shared.keyWindow?.rootViewController = vc
}
})

How best to remove all UIViewcontrollers and goto a single UIViewController?

I have an application with the option to change a users password. The reset password process is all done via web links. When the process is complete, the user returns to their app. Given that the password has now been changed, i would like to log the user out.
This is done when a user makes a request to the server for information and my server code will respond with a custom code to let the app know that the password has been changed. In this instance the code is '177'.
I have attempted to present the user with the 'login' uiviewcontroller - but without success. the screen just freezes. Please can someone advise?
if code == 177{
// INVALID API KET USED. LOG USER OUT
print("INVALID API KET USED. LOG USER OUT")
GlobalFunction.logout(action: {
// GO TO LOGIN VIEWCONTROLLER
print("GO TO LOGIN PAGE")
let loginVC = self.storyboard?.instantiateViewController(withIdentifier: "login") as! MyLoginViewController
self.present(loginVC, animated: true, completion: nil)
})
return
}
SECOND CLASS:
class GlobalFunction{
// LOG USER OUT OF APPLICATION
static func logout(action: #escaping (() -> ())){
// Remove logged in user credentials
UserDefaults.standard.removeObject(forKey: "userId")
UserDefaults.standard.removeObject(forKey: "firstname")
UserDefaults.standard.removeObject(forKey: "firstname")
UserDefaults.standard.removeObject(forKey: "api_key")
UserDefaults.standard.synchronize()
print("user logged out")
}
}
Make sure you have to run the UI events on main thread.
DispatchQueue.main.async {
let loginVC = self.storyboard?.instantiateViewController(withIdentifier: "login") as! MyLoginViewController
self.present(loginVC, animated: true, completion: nil)
}

Attempt to present TabBarController on FBSDKContainerViewController whose view in not in the window hierarchy

I'm using Facebook login in my app and I'm not able to go to next view after login completed.
i.e. TabBarController its shows an above stated error. I am doing custom login from a button. Here is my code of how I am doing login.
#IBAction func fbLoginBtn(sender: AnyObject) {
facebookLogin.logInWithReadPermissions(["email","user_friends","user_posts"],fromViewController: self, handler: { (response:FBSDKLoginManagerLoginResult!, error: NSError!) in
if error != nil {
print(error.localizedDescription)
print("Facebook login failed")
} else {
let accessToken = response.token.tokenString
print(accessToken)
print("Successfully login.\(accessToken)")
self.performSegueWithIdentifier("showMain", sender: self)
}
})
}
After login I'm performing segue to go to next view i.e. TabBarController. If anyone can help please help me. Thank you.

How to persist Firebase authdata after Force Quit (iOS)

I am using email + password authentication with Firebase for my app. Login works, and I use observeAuthEventWithBlock to check if a user is logged in - in order not to bring up the Login page. If I press the home button and open the app again, there is no problem. The problem I am having is if I force-quit the app. When I re-open, I have to log-in again.
Some notes about the setup before I show my login code.
There is a LoginViewController - not embedded - built w/ Storyboard
This is connected to a Navigation Controller
Which is what the first screen is embedded in, and the rest of the app uses this Nav Controller.
Login code:
#IBAction func loginButtonPressed() {
let userEmail = emailTextField.text
self.ref.authUser(self.emailTextField.text, password: self.passwordTextField.text, withCompletionBlock: { (error, auth) -> Void in
guard error == nil else {
if let errorCode = FAuthenticationError(rawValue: error.code) {
switch (errorCode) {
case .EmailTaken:
self.displayMessage("Email Error", theMessage: "This email is taken")
case .InvalidEmail:
self.displayMessage("Email Error", theMessage: "This email is invalid")
case .UserDoesNotExist:
self.displayMessage("User Error", theMessage: "A user account for email: \(userEmail!) does not exist")
case .InvalidPassword:
self.displayMessage("Password Error", theMessage: "The password is incorrect")
case .NetworkError:
self.displayMessage("Network Error", theMessage: "Seems like there's a problem with your internet connection")
default:
return
}
}
return //set Unknown Error Alert here
}
print("LOGGED IN: segue from loginButtonPressed")
self.userLoggedIn = true
print("user is logged in? \(self.userLoggedIn)")
self.performSegueWithIdentifier("loginLocaleSegue", sender: self)
})
}
Check if user is logged in - if so segue to navcon, pop it and display embedded View Controller:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if self.userLoggedIn.boolValue == true {
ref.observeAuthEventWithBlock { (authData) -> Void in
if authData != nil {
let navCon: UINavigationController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("MainNavigationController") as! UINavigationController
self.presentViewController(navCon, animated: false, completion: nil)
navCon.popViewControllerAnimated(false)
print("user is authenticated: \(authData.providerData["email"] as! String)")
print("segues from viewDidAppear")
} else {
return
}
}
}
}
I've seen questions related to Firebase auth which state that Authdata is stored in Keychain by default, which causes problems with Authdata persisting even after deletion of app, but I'm experiencing the total opposite issue. Any ideas?
From what I can tell, you're not writing "self.userLoggedIn = true" to any database, so it makes sense that it continues being "True" while you have the app on idle, but once you close the application, it then becomes nil (no value), this is because it's just chilling in the background while the app is open, but not completely closed. Try writing this to your Firebase database, as outlined in this tutorial, and see if that helps.
https://www.raywenderlich.com/109706/firebase-tutorial-getting-started

Resources