Xamarin.iOS Apps: Adding Login/Signup Capabilities
In today’s modern mobile apps it is very common for apps to require a user account in order to function. Apps like Twitter and Instagram require a user account in order to be used. So a very common User Experience is to show a Login Page when the app first starts up. The user should enter in their credentials, and then log into the app. If the user does not have any credentials, the app should offer them the ability to create an account, and then log them in.
Though this seems like a trivial task, it can be somewhat confusing if you are new to building iOS apps using Xamarin.iOS. In this article I’m going to show you an effective way to add a Login Page and a Signup Page to your Xamarin.iOS apps with a great user experience and a neat animation trick.
Full Source Code
You can download the full source code for this example app from my GitHub page. I’m not going into great detail into creating each individual view controller, but I will show you the important parts step by step.
Overview of the Sample App
A very common way to create user interfaces for iOS is by the use of Storyboards. I personally don’t like to use Storyboards to create my iOS UI as they are not very user friendly when it comes to sharing cross-platform code with Xamarin.Android apps. But storyboards are a very popular way to create user interfaces for Xamarin.iOS. Here is a high level overview of the sample app.
From the image above we can see that this application utilizes a UITabBarController controller as the Initial Root View Controller. The UITabBarController is the main page the user is directed to after they successfully log into the app. The UITabBarController also has two Tabs that are encapsulated by two UINavigationControllers. This allows subsequent pages to be pushed onto the Navigation Stack, such as the Tab 1 View Controller.
In MainStoryboard.storyboard, you can also see a LoginPageViewController as well as a SignUpViewController. You can see that there is no direct way get to the LoginPageViewController or the SignupViewController from your UITabBarController. This is done intentionally as we will show the LoginPageViewController programmatically. But fist, let’s discuss the most important parts in detail.
LoginPageViewController
The LoginPageViewController consists of two UITextFields. One for the Username, and the other for the Password. In addition, we have two UIButtons. One button is to log into the app, and the other is for showing the user the Sign Up page so they can create an account. Now let’s take a look at the codebehind for LoginPageViewController.cs
partial class LoginPageViewController : UIViewController
{
//Create an event when a authentication is successful
public event EventHandler OnLoginSuccess;
public LoginPageViewController (IntPtr handle) : base (handle)
{
}
partial void LoginButton_TouchUpInside(UIButton sender)
{
//Validate our Username & Password.
//This is usually a web service call.
if(IsUserNameValid() && IsPasswordValid())
{
//We have successfully authenticated a the user,
//Now fire our OnLoginSuccess Event.
if(OnLoginSuccess != null)
{
OnLoginSuccess(sender, new EventArgs());
}
}
else
{
new UIAlertView("Login Error", "Bad user name or password", null, "OK", null).Show();
}
}
private bool IsUserNameValid()
{
return !String.IsNullOrEmpty(UserNameTextView.Text.Trim());
}
private bool IsPasswordValid()
{
return !String.IsNullOrEmpty(PasswordTextView.Text.Trim());
}
}
The first thing to note is we create an Event called OnLoginSuccess. When the user taps on the “Login” button, the app will validate the user name and password. If validation is successful, then we will fire the OnLoginSuccess Event. Notice here that we want to keep this Login page loosely coupled and we fire an event after we successfully authenticate our user.
This ViewController will leave it up to another client to do whatever it wants after we authenticate our user. This gives us the flexibility to show different View Controllers after we log in. For example, if we have two different storyboards, one for iPhones, and the other for iPads, this gives us the flexibility to show either ViewController/Storyboard without tightly coupling our Login Page to another View Controller. The LoginPageViewController doesn’t, and shouldn’t care what it should show next. That should be decided else where.
The Application Delegate
[Register("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
private bool isAuthenticated = false;
public override UIWindow Window
{
get;
set;
}
//Public property to access our MainStoryboard.storyboard file
public UIStoryboard MainStoryboard
{
get { return UIStoryboard.FromName("MainStoryboard", NSBundle.MainBundle); }
}
//Creates an instance of viewControllerName from storyboard
public UIViewController GetViewController(UIStoryboard storyboard, string viewControllerName)
{
return storyboard.InstantiateViewController(viewControllerName);
}
//Sets the RootViewController of the Apps main window with an option for animation.
public void SetRootViewController(UIViewController rootViewController, bool animate)
{
if(animate)
{
var transitionType = UIViewAnimationOptions.TransitionFlipFromRight;
Window.RootViewController = rootViewController;
UIView.Transition(Window, 0.5, transitionType,
() => Window.RootViewController = rootViewController,
null);
}
else
{
Window.RootViewController = rootViewController;
}
}
//Override FinishedLaunching. This executes after the app has started.
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
//isAuthenticated can be used for an auto-login feature, you'll have to implement this
//as you see fit or get rid of the if statement if you want.
if(isAuthenticated)
{
//We are already authenticated, so go to the main tab bar controller;
var tabBarController = GetViewController(MainStoryboard, "MainTabBarController");
SetRootViewController(tabBarController, false);
}
else
{
//User needs to log in, so show the Login View Controlller
var loginViewController = GetViewController(MainStoryboard, "LoginPageViewController") as LoginPageViewController;
loginViewController.OnLoginSuccess += LoginViewController_OnLoginSuccess;
SetRootViewController(loginViewController, false);
}
return true;
}
void LoginViewController_OnLoginSuccess (object sender, EventArgs e)
{
//We have successfully Logged In
var tabBarController = GetViewController(MainStoryboard, "MainTabBarController");
SetRootViewController(tabBarController, true);
}
}
In order to show the LoginPage, we need to make some modifications to our AppDelegate.cs file. Remember, the AppDelegate is created for every Xamarin.iOS Project, and is the root object of your application. When you create a Xamarin.iOS Storyboard application, the AppDelegate is automatically created, and will launch your MainStoryboard.storyboard InitialViewController (as set in the storyboard).
The first thing we add is a private property called isAuthenticated. This is actually optional, but if you want add AutoLogin capability, this is one way or doing it. I don’t keep track if the user is automatically logged in this app. So I just set this to false for simplicity. If you need this capability, you would need a way to store a flag if the user wants to automatically log in every time they start the app.
The second thing we need to add is a public property called MainStoryboard. This property will programmatically return an instance our our MainStoryboard.storyboard file. We are going to call this several times since all of our UIViewControllers are defined in MainStoryboard.storyboard.
The third thing we need to add is a public method called GetViewController(). From the code above you can see that GetViewController() has two parameters. The first parameter is an instance of the Storyboard where our ViewController is defined. The second parameter is the name of the ViewController (aka StoryboardID) we want to instantiate. With these two parameters, we can return an instance of any ViewController that is defined in the given Storyboard. Remember to set the StoryboardID for each ViewController you want to dynamically instantiate.
The fourth thing we need to add is a public method called SetRootViewController(). SetRootViewController() requires two parameters. The first parameter is an instance of our new RootViewController. The second parameter is a boolean value. If true is passed in, when we animate a transition when we change the RootViewController of our app. If false, no animation occurs. Also note the transition type. I set it to UIViewAnimationOptions.TransitionFlipFromRight as it creates cool looking flip effect from right to left. But you can set it to any transition type you want. Just choose another enumeration from UIViewAnimationOptions.
The fifth and final thing we need to do is override the FinishedLaunching method(). This method will execute after the application has finished launching, and is ready to display our initial view controller. By default, this method is not overriden, and will load the ViewController that is set as the “Initial View Controller” in MainStoryboard.storyboard. But in this case, we want to dynamically set our RootViewController to our LoginPageViewController if our user is not already authenticated (i.e. Auto-Login is not enabled). If our user is already authenticated (i.e. Auto-Login is enabled) we want to set our RootViewController to our UITabBarController.
You’ll also notice when we create an instance of the LoginPageViewController, we also wire an event for the OnLoginSuccess event (remember we defined this earlier). So in our event handler, we want to create an instance of our UITabBarController, and set it as the application RootViewController.
So, when the application start up, we programmatically determine if we want to show the login page or not by setting the RootViewController of the application. You’ll notice that we pass in false as the animation boolean parameter for SetRootViewController(). Animation isn’t necessary when we start up. But for the OnLoginSuccess event handler, it is nice to have a cool animation effect when the user taps on the Login Button.
Logging Out
After you start the app, tap on the second tab on the UITabBar. This will show the TabPage2ViewController. On that view controller, you’ll see a log out button. This is a simple implementation, but can bind this to anything you want, include a button on the Navigation Bar or a slide out menu option.
partial class TabPage2ViewController : UIViewController
{
public TabPage2ViewController (IntPtr handle) : base (handle)
{
}
//Implement a Logout Feature
partial void LogOutButton_TouchUpInside(UIButton sender)
{
//Create an instance of our AppDelegate
var appDelegate = UIApplication.SharedApplication.Delegate as AppDelegate;
//Get an instance of our MainStoryboard.storyboard
var mainStoryboard = appDelegate.MainStoryboard;
//Get an instance of our Login Page View Controller
var loginPageViewController = appDelegate.GetViewController(mainStoryboard, "LoginPageViewController") as LoginPageViewController;
//Wire our event handler to show the MainTabBarController after we successfully logged in.
loginPageViewController.OnLoginSuccess += (s, e) =>
{
var tabBarController = appDelegate.GetViewController(mainStoryboard, "MainTabBarController");
appDelegate.SetRootViewController(tabBarController, true);
};
//Set the Login Page as our RootViewController
appDelegate.SetRootViewController(loginPageViewController, true);
}
}
So now that we’ve defined all of our logic in our AppDelegate to instantiate our view controllers and to set the RootViewController of our application, we want to do this outside our AppDelegate. When the user taps on the Logout Button, we need to set the application RootViewController back to our Login Page.
Looking at the code above, we get a reference to our Application Delegate. Since the AppDelegate is a Singleton, there is no need to create an instance. We just need to get the current reference from our UIApplication. Second we get an instance of our MainStoryboard.storyboard from our app delegate. From that we can create a new instance of the LoginPageViewController, set the OnLoginSuccess event handler, and then set it as our new RootViewController. Notice in this case when we call SetRootViewController(), we pass in true, for our animation effect (pretty cool looking isn’t it?).
Signing Up For An Account
So now the user has been logged out of our app and we see the Login page. If you tap on the “Signup” button, this will create an instance of the Signup View Controller. If you look at the code behind for SignUpViewController, you will see pretty much the same pattern.
partial class SignUpViewController : UIViewController
{
public SignUpViewController (IntPtr handle) : base (handle)
{
}
//This assumes we have successfully create a new user account
//Naturally, you'll add your logic here, but we're ignoring
//that for simplicity.
partial void SignUpButton_TouchUpInside(UIButton sender)
{
//Create an instance of our AppDelegate
var appDelegate = UIApplication.SharedApplication.Delegate as AppDelegate;
//Get an instance of our MainStoryboard.storyboard
var mainStoryboard = appDelegate.MainStoryboard;
//Get an instance of our MainTabBarViewController
var mainTabBarViewController = appDelegate.GetViewController(mainStoryboard, "MainTabBarController");
//Set the MainTabBarViewController as our RootViewController
appDelegate.SetRootViewController(mainTabBarViewController, true);
}
partial void CancelButton_TouchUpInside(UIButton sender)
{
DismissViewController(true, null);
}
}
The pattern is the same as the logout implementation. We just get a reference to our app delegate, instantiate the proper ViewController, and then set it as our RootViewController.
Summary
In this article I showed you an effective way to show a user a login page by swapping the application RootViewController. In addition, I also showed you how to add an optional animation effect when you set the app RootViewController. This enhances the user experience, and makes the transition less “jaggy”. We implemented all of this login in the AppDelegate.cs file, and we referenced the AppDelegate in other parts of the app.
I hope you found this useful and feel free to leave me a comment below with any questions. And download the source code and feel free to dissect the code and play around with it.