Login and UITabBarController


Common part of lots of apps is user authentication flow. When app is started, login screen is presented and user have chance to enter right username and password combination to see content in which he is interested.

There are a lot of ways to show that content to user, but we are going to think about tab based navigation pattern. If you have tab based navigation and user authentication it can be tricky to implement those parts in meaningful way.

Lets assume that we have following:

Now, the very first thing that comes to mind is to have UINavigationController initialized with LoginViewController. That navigation controller is set as window’s rootViewController. Once when user is logged in, we just push our UITabBarController instance. But, we have to be careful here. Documentation of UITabBarController states:

When deploying a tab bar interface, you must install this view as the root of your window. Unlike other view controllers, a tab bar interface should never be installed as a child of another view controller.

So, we should set window’s rootViewController property to UITabBarController instance. But, what should we do with login part? We could present it as modal view controller, but that doesn’t feel right. We would then have living main interface without user. Also, view controller lifecycle methods are called only once and it would be handy to get full lifecycle calls every time when user logs in. Our main interace doesn’t make sense without user, so we should instantiate it once we have user.

Nice solution to this problem can be changing rootViewController once user is logged in. The idea is to have two storyboards. “Main” used for main interface once user is logged in, and “Login” used when user is not logged in. We are going to change window’s rootViewController depending on if user is logged in or not.

Lets start with AppDelegate implementation

window = UIWindow(frame: UIScreen.mainScreen().bounds)
if loggedInUser == nil {
    let storyboard = UIStoryboard(name: "Login", bundle: nil)
    window?.rootViewController = storyboard.instantiateInitialViewController() as? UIViewController
} else {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    window?.rootViewController = storyboard.instantiateInitialViewController() as? UIViewController
}
window?.makeKeyAndVisible()

This is very simple, if user is already logged in (we stored it on disk previously or something similar), then we should show main interface. Otherwise, show login interface.

For convenience, we are going to create UIViewController extension:

extension UIViewController {
    
    func transitionToMainViewController() {
        
        if let window = self.view.window {
            UIView.transitionWithView(window, duration: 0.3, options: UIViewAnimationOptions.TransitionFlipFromLeft, animations: {
                let storyboard = UIStoryboard(name: "Main", bundle: nil)
                window.rootViewController = storyboard.instantiateInitialViewController() as? UIViewController
                }, completion: nil)
            }
    }
    
    func transitionToLoginViewController() {
        
        if let window = self.view.window {
            UIView.transitionWithView(window, duration: 0.3, options: UIViewAnimationOptions.TransitionFlipFromLeft, animations: {
                let storyboard = UIStoryboard(name: "Login", bundle: nil)
                window.rootViewController = storyboard.instantiateInitialViewController() as? UIViewController
                }, completion: nil)
        }
    }
}

It is also nice that we can choose from set of predefined animation options:

Now, it is very simple to show “Login” and “Main” interface when we need them:

func login() {
	webService.login(username: username, password: password) { (result) in
    	if result == success {
    		// save user, ...
    		self.transitionToMainViewController()
    	}
	}
}

func logout() {
	// clean user, ...
	self.transitionToLoginViewController()
}

And that should be it, with few lines of code we have sane flow and all lifecycle methods are being called every time when user log in. If you are not fan of storyboards, you don’t have to use them to achive this, similar behaviour can be achived with xibs.

You can download example project here