How to move login page when session expired - ios

I have multiple screens and api's, if session expired each api get session expired message. Based in that I'm moving from current page to login page.
My code :
//If session expaired move to login page
if message == "Session Expired" {
//Session Expired
DispatchQueue.main.async {
let lpc = self.storyboard?.instantiateViewController(withIdentifier: "LVC")
//Set the user login key false
UserDefaults.standard.set(false, forKey: "isUserLoggedIn")
//Clear user defaults
SharedClass.sharedInstance.clearDataFromUserDefaults()
self.navigationController?.pushViewController(lpc!, animated: false)
}
}
Here I'm using ** pushViewController** to navigate back. Every thing working fine, but when i'm in Nth VC is session expired it's navigating N times to login page. Means if I'm navigating from 1st VC to 2nd VC, 2nd VC to 33rd VC, 3rd VC to 4th, is session expired in 4th VC it's navigating to login page around 3times. How to resolve this issue....

You can try this
let vc = self.storyboard?.instantiateViewController(withIdentifier: "LVC")
self.navigationController?.setViewControllers([vc], animated: true)

Since you didn't provide your full code so I am not sure why that issue happens. However, here are 2 solutions to achieve your requirement.
First implementation: Image for 1st flow
If the user logged in:
navigationController?.viewControllers = [loggedInVC]
If the user doesn't login yet or when he/she logout:
navigationController?.viewControllers = [logInVC]
2nd implementation: Image for 2nd flow
LoginVC is the root view controller and acts as a loading screen, and only show the login form when the session is invalid. With this implementation, every time the session is invalid. You can call:
navigationController?.popToRootViewController(animated: true)
to navigate the user back to the login screen.

First create the global variable to access the AppDelegate, because we are putting redirection function here.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
Now put below function in AppDelegate to set login screen whenever your session is expired.
func configureWindow(_ viewController: UIViewController) {
if let window = window {
window.rootViewController = viewController
window.makeKeyAndVisible()
}
}
now set your login UIViewController as like below.
let loginVC = LoginViewController()
appDelegate.configureWindow(loginVC)

use the following:
self.navigationController?.popToRootViewController(animated: false)
if login is not root,then you can use the following:
if let controllers = self.navigationController?.viewControllers, controllers.count > 0{
for vc in controllers{
if vc is LoginViewController{
self.navigationController?.popToViewController(vc, animated: false)
}
}
}

I don't know is this right or wrong. But i'm getting one more issue
1) First i cleared all navigations from navigation stack
2) Then i made my LoginVC as Root VC
3) Finally i called popToRootViewController
//If session expaired move to login page
if message == "Session Expired" {
DispatchQueue.main.async {
//Check navigation stacks
let navigationArray = self.navigationController?.viewControllers //To get all UIViewController stack as Array
print(navigationArray!)//Prints navigation stacks
//Remove all
self.navigationController!.viewControllers.removeAll()
//Check navigation stacks
let navigationArray2 = self.navigationController?.viewControllers //To get all UIViewController stack as Array
print(navigationArray2 as Any)//Prints nill
//Check whether the user logined or not
UserDefaults.standard.set(false, forKey: "isUserLoggedIn")
//Clear user defaults
SharedClass.sharedInstance.clearDataFromUserDefaults()
let lvc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LVC") as! LoginViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = lvc
}
}
//Call alert function
self.showAlert(title: "", msg: message)
I don't know is this right or wrong. But i'm getting one more issue
Warning: Attempt to present <UIAlertController: 0x7fb840800600> on <******: 0x7fb840069800> whose view is not in the window hierarchy!

Related

I am having a problem with checking if my users are signed in to Firebase and changing the the initial view controller

My goal is to check if my users have signed in to my app in Firebase before. Then change the initial view controller from my Navigation controller to my TabBarController. I'd like to do this for a better user experience so they don't have to log in every time.
Also where is the best place to put this code? my first View controller or my app Delegate?
if Auth.auth().currentUser != nil {
// User is signed in.
func transitionToTab() {
let tabBarController =
storyboard?.instantiateViewController(identifier: Constants.Storyboard.TabBarController) as? TabBarController
view.window?.rootViewController = tabBarController
view.window?.makeKeyAndVisible()
}
} else {
// No user is signed in.
func tranitionToView() {
_ =
storyboard?.instantiateViewController(identifier: Constants.Storyboard.HomeViewController) as? ViewController
view.window?.makeKeyAndVisible()
}
}
Your problem is you don't call the functions within the if/else statement.
I would recommend creating an additional slash screen that replicates the one within launchscreen.storyboard to initiate your authentication process. Your code should look similar to this;
if Auth.auth().currentUser != nil {
// User is signed in.
let tabBarController =
storyboard?.instantiateViewController(identifier: Constants.Storyboard.TabBarController) as? TabBarController
view.window?.rootViewController = tabBarController
view.window?.makeKeyAndVisible()
} else {
// No user is signed in.
storyboard?.instantiateViewController(identifier: Constants.Storyboard.HomeViewController) as? ViewController
view.window?.makeKeyAndVisible()
}
you can use this function from app delegate method
"didFinishLaunchingWithOptions"
and make an userdefaults to store current user object or any check that you
will true once user login into the app and in Appdelegate "didFinishLaunchingWithOptions"
check if it's true than set initial page
tabbarcontroller else show login screen.

Tab bar not showing on home page after login

I have implemented a firebase login and want to show my home page with a function, my problem is when showing the home page it will not show the tab bar and when showing the tab bar as initial VC it shows blank, but when I set the tab bar controller as initial VC within the settings it will show the tab bar but bypass the login, how can I get my home page to show the tab bar within my function?
#IBAction func LoginButtonTapped(_ sender: Any) {
// validate feilds still to impliment
//if all feilds valid do below
let email = EmailFeild.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = PasswordFeild.text!.trimmingCharacters(in: .whitespacesAndNewlines)
// sign in user
Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
if error != nil{
// sign in error
self.ErrorMsg.text = error!.localizedDescription
self.ErrorMsg.alpha = 1
}else{
// go to home screen
self.transitionHome()
}
}
}
func transitionHome(){
let homeViewController =
storyboard?.instantiateViewController(identifier: Constants.Storyboards.homeViewContrl) as? HomeViewController
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
my attempt at setting tab bar as root VC leads to a black screen but when i set it as inital VC and bipass the login the home view shows the tab bar
// let tabViewController =
// storyboard?.instantiateViewController(identifier: Constants.Storyboards.homeNav) as?
//MainTabBarController
//view.window?.rootViewController = tabViewController
//view.window?.makeKeyAndVisible()
}
}
You just need to add UITabBarController in between, assign it to Class property of your TabController in storyboard and set it to root instate of directly using UIViewController. Refer below code snippet:
let tabViewController =
storyboard?.instantiateViewController(withIdentifier: "TabController") as? TabController
view.window?.rootViewController = tabViewController
view.window?.makeKeyAndVisible()
It will work as expected!!

How to present different view controllers from appdelegate in ios

Actually I am having navigationcontroller as root controller it is embed in main.storyboard,i am having two screens one screen login and another one home as per login credentials i need to skip login screen and i need to show home screen.From appdelegate i am doing this skipping it is not working properly
Unbalanced calls to begin/end appearance transitions for <UINavigationController: 0x7fadf384c600>.
let storyboard=UIStoryboard.init(name: "Main", bundle: nil)
let navigationController=storyboard.instantiateInitialViewController()
let username=UserDefaultUtil.getString(key: AppConstants.PREF_USERID)
print(username!)
if username != ""
{
window?.rootViewController=navigationController
let sectionController=SectionController(nibName: "SectionController" , bundle: nil)
navigationController?.present(sectionController, animated: true, completion: nil)
}
I guess you are trying to present your sectionController in navigationController, its not really how it works, try this code:
let navigationController = self.storyboard?.instantiateInitialViewController() as! UINavigationController
and replace the present with this:
navigationController.setViewControllers([sectionController], animated: false)
or just drop the navigationController instantiate and create it with code and set it as window?.rootViewController:
let sectionController=SectionController(nibName: "SectionController" , bundle: nil)
let nav = UINavigationController(rootViewController: sectionController)
window?.rootViewController = nav
First, check the user credentials in the login page. Then use:
if hasCredentials {
let vc:AnyObject! = self.storyboard?.instantiateViewController(withIdentifier: "someViewController")
self.show(vc as! UIViewController, sender: vc)
}
Sidenote: Personally, I do this from the login page because it simplifies the process and I do not like having weight sitting in my AppDelegate. If you were thinking you did not want people seeing your login screen who are already members, you can do it from an AppDelegate, but take into account the user experience might be diminished during the loading process if this is the route you decide to take.

iOS how to change rootViewController when dismissController

my storyboard
If user doesn't login, the rootViewController is Login
after user login done, rootViewController is MainTabBarController
I have done that
But, I have encounter question is Logout
My Logout is dismissViewController
If my rootViewController is Login, it works
It will remove current ViewController, so Login appear
But when my rootViewController is MainTabBarController, dismiss is not work, I've try using poptoRootViewController in vain.
what should I do in Logout ?
I want to do like this
dismissController(true,{
rootViewController = `Login`
})
For Logout do following:- (Add below code inside IBAction or didSelect ..etc method where logout is called)
// Making Login as rootViewController as user is no longer logged in
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "isUserLoggedIn")
NSUserDefaults.standardUserDefaults().synchronize()
let loginVC = self.storyboard?.instantiateViewControllerWithIdentifier("Login") as! loginViewController
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDel.window?.rootViewController = loginVC
Also add following in AppDelegate:-
// Checking user login status, if user already logged in then making main tab bar view controller as root view controller
let userLoginStatus = NSUserDefaults.standardUserDefaults().boolForKey("isUserLoggedIn")
if(userLoginStatus)
{
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let centerVC = mainStoryBoard.instantiateViewControllerWithIdentifier("MainTabBar") as! ViewController
window!.rootViewController = centerVC
window!.makeKeyAndVisible()
}
And Also where login Validation is done, after validating user credentials:-
#IBAction func loginTapped(sender: AnyObject) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let centerVC = mainStoryBoard.instantiateViewControllerWithIdentifier("MainTabBar") as! ViewController
// Important to set status to true
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isUserLoggedIn")
NSUserDefaults.standardUserDefaults().synchronize()
appDel.window!.rootViewController = centerVC
appDel.window!.makeKeyAndVisible()
}
NOTE:- Don't forget to add STORYBOARD IDs for required View Controllers to instiantiate them
You just need to make a function in AppDelegate and call that function on logout.
eg.(Obj-C Version)
- (void) setCurrentRootController : (UIViewController *)viewController {
[[[UIApplication sharedApplication].delegate window] setRootViewController:nil];
UINavigationController *navigation = [[UINavigationController alloc]initWithRootViewController:viewController];
[[[UIApplication sharedApplication].delegate window] setRootViewController:navigation];
}
When you are doing logout you just need to set rootViewController and then call popToRootViewController method.
Hope this will work for you !!
You don't need to use any additional technics to set rootViewController in runtime except following:
UIApplication.sharedApplication().keyWindow?.rootViewController = viewController;
You can wrap this with animation if you want
In Swift You can do like this
let vc: UIViewController! = self.storyboard!.instantiateViewControllerWithIdentifier("LoginViewController")
let window = UIApplication.sharedApplication().windows[0];
window.rootViewController = vc;
You can maintain two windows, one is for login, and second one for the users who got authenticated.
This way you can easily switch btw windows rather than having a messy MVC for it.
Warning, make sure your are in the main thread if you use Delegate or Completion methods.
Code in Swift 3
DispatchQueue.main.async {
guard let tb = self.storyboard?.instantiateViewController(withIdentifier: "LoggedUser") as? UITabBarController else {
print("Could not instantiate view controller with identifier of type LoggedUser")
return
}
self.present(tb, animated: true, completion: nil)}

Instagram clone app bypasses login page even if user is not logged in

I am creating an instagram clone app. I have a login view controller which redirects the user to the table view controller after the user logs in.
Also, the user doesn't have to log in if he's already logged in. I am using the following code to achieve that.
override func viewDidAppear(animated: Bool) {
if PFUser.currentUser() != nil {
self.performSegueWithIdentifier("userlist", sender: self)
}
}
This works perfectly fine. However this happens even when a user is NOT logged in! How do i fix this?
Thanks in advance.
Put this code in your App Delegate, just before the "return true" in didFInishLaunchingWithOptions:
let currentUser = PFUser.currentUser()
if currentUser != nil {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyBoard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let controller1 = storyBoard.instantiateViewControllerWithIdentifier("yourControllerstoryboardID") as! yourController
self.window?.rootViewController = controller1
self.window?.makeKeyAndVisible()
}
Note that in order for this to work, you have to give the view controller--the one you want to jump to if there IS a user, a storyboard ID. You do this in the inspector panel.
this will make it so that if there is a current user, it will jump past the login screen. Otherwise, it will show the log in screen.

Resources