Reputation: 31
Greetings StackOverflow!
I'm really new to Swift development, so don't go all crazy on me please :3
I've created a login page for my mobile app (Firebase Email + Password)
I've implemented an "auto login" function - so when a user logs in for the first time, the user stays logged after closing the app & opening it.
The problem is that every time a user opens the app, there is a slight delay between viewDidLoad
and viewDidAppear
-> resulting in that every time the app opens, you can see the login screen for about 0.4 sec until it automatically sings the user in.
Once the user logs in the user are segued (no animation) to my UITabBarController
.
I've provided the code for my LoginViewController
.
LoginViewController is my first Controller the app loads in.
Best regards
class LoginViewController: UIViewController {
@IBOutlet weak var loginEmail: UITextField!
@IBOutlet weak var loginPassword: UITextField!
@IBOutlet weak var loginBorder: UIButton!
let userDefault = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
if userDefault.bool(forKey: "usersignedin") {
performSegue(withIdentifier: "login", sender: self)
}
//Border color button//
loginBorder.layer.borderColor = UIColor.white.cgColor
//Border color button//
//Hide Keyboard Use
self.hideKeyboardWhenTappedAround()
//Hide Keyboard Use
//start padding function for login
addPaddingAndBorder(to: loginEmail)
addPaddingAndBorder(to: loginPassword)
//start padding function for login
}
@IBAction func loginButton(_ sender: UIButton) {
Auth.auth().signIn(withEmail: loginEmail.text!, password: loginPassword.text!) { (user, error) in
if user != nil {
self.userDefault.set(true, forKey: "usersignedin")
self.userDefault.synchronize()
self.performSegue(withIdentifier: "login", sender: self)
} else {
let alert = UIAlertController(title: "Invalid Email or Password", message: nil, preferredStyle: .alert)
let okButton = UIAlertAction(title: "Ok", style: .default, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
}
}
}
}
Upvotes: 3
Views: 764
Reputation: 764
Best way is to decide in AppDelegate which controller to load based on Login Status
let navigationController = UINavigationController()
navigationController.navigationBar.barStyle = .black
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if UserDefaults.standard.object(forKey: "Token") != nil{
let initialViewController = storyboard.instantiateViewController(withIdentifier: "HomeViewController")
navigationController.viewControllers = [initialViewController]
} else{
let initialViewController = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
navigationController.viewControllers = [initialViewController]
}
navigationController.navigationBar.barTintColor = UIColor.black
navigationController.navigationBar.tintColor = UIColor.white
navigationController.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
Upvotes: 1
Reputation: 753
We're doing something similar to this in my current project, but we decide which controller to show first in the AppDelegate
.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
var storyboard: UIStoryboard!
if isLoggedIn {
storyboard = UIStoryboard(name: "main", bundle: nil)
}
else {
storyboard = UIStoryboard(name: "login", bundle: nil)
}
let viewController = storyboard.instantiateInitialViewController()!
viewController.loadViewIfNeeded()
window.rootViewController = viewController
}
This has the advantage that when the app loads it immediately take you to the correct ViewController
without showing the wrong one briefly first.
Upvotes: 4
Reputation: 398
viewDidAppear(_:)
happens right after the view appears. I suggest moving your code from viewDidAppear(_:)
to viewWillAppear(_:)
.
you can also add your login code in the viewDidLoad()
because this method is called even before viewWillAppear(_:)
.
in your case your code will be:
override func viewDidLoad() {
super.viewDidLoad()
if userDefault.bool(forKey: "usersignedin") {
performSegue(withIdentifier: "login", sender: self)
}
}
when a view is loading, there are different methods which get called in a specific order:
viewDidLoad()
(once and only when the object is being created.)viewWillAppear(_:)
viewDidAppear(_:)
when you push another view controller over this one, this view controller won't be removed from the view controllers' stack. the important thing is when you come back to this view controller, this time viewDidLoad()
won't get called. so only these methods get called again in this order:
viewWillAppear(_:)
viewDidAppear(_:)
Upvotes: 1
Reputation: 24341
Move the call to performSegue(withIdentifier:sender:)
to viewWillAppear(_:)
instead of viewDidAppear(_:)
in case usersignedin = true
.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if userDefault.bool(forKey: "usersignedin") {
performSegue(withIdentifier: "login", sender: self)
}
}
Also, the response of signIn
method is not received on the main thread
. Since you're not updating the UI
on the main thread
, that's why it is resulting in the delay.
Embed the UI
part in closure
of signIn
method in DispatchQueue.main.async
Auth.auth().signIn(withEmail: loginEmail.text!, password: loginPassword.text!) { (user, error) in
if user != nil {
self.userDefault.set(true, forKey: "usersignedin")
self.userDefault.synchronize()
DispatchQueue.main.async { //here..........
self.performSegue(withIdentifier: "login", sender: self)
}
}
else {
let alert = UIAlertController(title: "Invalid Email or Password", message: nil, preferredStyle: .alert)
let okButton = UIAlertAction(title: "Ok", style: .default, handler: nil)
alert.addAction(okButton)
DispatchQueue.main.async { //here..........
self.present(alert, animated: true, completion: nil)
}
}
}
Upvotes: 1
Reputation: 21
Change the order of view controllers. Always load your UITabBarController
first (as initial controller). Then in your UITabBarController
viewDidLoad
, check if the user is logged-in or not. If not, then show the login screen. This way your UITabBarController
is always loaded first and then based on auto-logn condition, your login screen is displayed. So when the user is auto looged-in then your delay issue will not come.
I have used this concept in all of my apps and it works fine.
Upvotes: 1
Reputation: 3089
This is correct system behavior. ViewDidLoad
will always be called much sooner than ViewDidAppear
. IF you want to change the base view controller based on userDefault.bool(forKey: "usersignedin")
, add that logic to where you set the main view controller and swap them if already signed in.
Upvotes: 1