Ios – Best practices for Storyboard login screen, handling clearing of data upon logout

authenticationioslogoutuistoryboard

I'm building an iOS app using a Storyboard. The root view controller is a Tab Bar Controller. I'm creating the login/logout process, and it's mostly working fine, but I've got a few issues. I need to know the BEST way to set all this up.

I want to accomplish the following:

  1. Show a login screen the first time the app is launched. When they login, go to the first tab of the Tab Bar Controller.
  2. Any time they launch the app after that, check if they are logged in, and skip straight to the first tab of the root Tab Bar Controller.
  3. When they manually click a logout button, show the login screen, and clear all the data from the view controllers.

What I've done so far is set the root view controller to the Tab Bar Controller, and created a custom segue to my Login view controller. Inside my Tab Bar Controller class, I check whether they are logged in inside the viewDidAppear method, and a perform the segue: [self performSegueWithIdentifier:@"pushLogin" sender:self];

I also setup a notification for when the logout action needs to be performed: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

Upon logout, I clear the credentials from the Keychain, run [self setSelectedIndex:0] and perform the segue to show the login view controller again.

This all works fine, but I'm wondering: should this logic be in the AppDelegate? I also have two issues:

  • The first time they launch the app, the Tab Bar Controller shows briefly before the segue is performed. I've tried moving the code to viewWillAppear but the segue will not work that early.
  • When they logout, all the data is still inside all the view controllers. If they login to a new account, the old account data is still displayed until they refresh. I need a way to clear this easily on logout.

I'm open to reworking this. I've considered making the login screen the root view controller, or creating a navigation controller in the AppDelegate to handle everything… I'm just not sure what the best method is at this point.

Best Answer

Your storyboard should look like this

In your appDelegate.m inside your didFinishLaunchingWithOptions

//authenticatedUser: check from NSUserDefaults User credential if its present then set your navigation flow accordingly

if (authenticatedUser) 
{
    self.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];        
}
else
{
    UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"];
    UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];

    self.window.rootViewController = navigation;
}

In SignUpViewController.m file

- (IBAction)actionSignup:(id)sender
{
    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    appDelegateTemp.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
}

In file MyTabThreeViewController.m

- (IBAction)actionLogout:(id)sender {

    // Delete User credential from NSUserDefaults and other data related to user

    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"];

    UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];
    appDelegateTemp.window.rootViewController = navigation;

}

Swift 4 Version

didFinishLaunchingWithOptions in app delegate assuming your initial view controller is the signed in TabbarController.

if Auth.auth().currentUser == nil {
        let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "WelcomeNavigation")
        self.window?.rootViewController = rootController
    }

    return true

In Sign up view controller:

@IBAction func actionSignup(_ sender: Any) {
let appDelegateTemp = UIApplication.shared.delegate as? AppDelegate
appDelegateTemp?.window?.rootViewController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateInitialViewController()
}

MyTabThreeViewController

 //Remove user credentials
guard let appDel = UIApplication.shared.delegate as? AppDelegate else { return }
        let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "WelcomeNavigation")
        appDel.window?.rootViewController = rootController