Fat Fractal Tutorial for iOS: Getting Started

Learn how to make an iOS app with a back end like Twitter in this beginner Fat Fractal tutorial! By .

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 6 of this article. Click here to view the first page.

Building The Authentication System

Next let’s build your authentication system.

Open SignupViewController.m, and add the following code to signupButtonPressed: right after the “TODO” comment:

// Step 1
FFUser *newUser = [[FFUser alloc] initWithFF:[FatFractal main]];
newUser.firstName = fullname;
newUser.userName = username;
newUser.email = email;

// Step 2
[[FatFractal main] registerUser:newUser password:password onComplete:^(NSError *theErr, id theObj, NSHTTPURLResponse *theResponse) {

    // Step 3
    if (theErr) {
        [MBProgressHUD hideHUDForView:self.view animated:YES];
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:[theErr localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert show];
        return;
    } else {
        // Step 4
        if (theObj) {
            // App is now in signed in state.

            // Step 5
            [self saveUserCredentialsInKeychain];

            [MBProgressHUD hideHUDForView:self.view animated:YES];

            // Step 6
            [self dismissViewControllerAnimated:YES completion:^{
                [self handleSuccessfulSignup];
            }];
        }
    }
}];

The above code registers a new user. Let’s go over it step by step:

  1. Here you create a new FFUser instance using the FatFractal singleton. You then assign the properties of the FFUser as the user input in the textfields
  2. You then call the registerUser:password:onComplete: method on your FatFractal singleton. This method will attempt to sign up a user, and provide a callback when the backend server replies to you.
  3. If there is an error, you hide the progress view and display an alert.
  4. If there is no error, you double check that the server returned you an object, which is your FFUser object actually. Your app is now in a signed in state.
  5. You then save the user’s credentials in your keychain so you can automatically log the user in from hence forth.
  6. You then dismiss the modal view controller, and send your delegate a message – that the sign up was successful.

Next inside AppDelegate.h, declare a new class method:

+ (BOOL)checkForAuthentication;

Inside AppDelegate.m, implement this method:

+ (BOOL)checkForAuthentication {
    if ([_ff loggedIn] || ([_keychainItem objectForKey:(__bridge id)(kSecAttrAccount)] != nil && ![[_keychainItem objectForKey:(__bridge id)(kSecAttrAccount)] isEqual:@""])){
        NSLog(@"checkForAuthentication: FFUser logged in.");
        return YES;
    } else {
        NSLog(@"checkForAuthentication: No user logged in.");
        return NO;
    }
}

Inside MainFeedViewController.m, import AppDelegate.h:

#import "AppDelegate.h"

Then inside checkForAuthentication replace the line if (false) { with the following:

if ([AppDelegate checkForAuthentication]) {

This checks to see if the user is logged in, and if not presents the WelcomeViewController.

And with this, you can now sign up new users for your application! >Build and run and try to sign up a new user. You should see the modal Welcome View Controller dismiss itself and show you your MainFeedViewController table view if successful.

MainFeedViewControllerAfterRunningSignup

Not bad for just a few lines of code, eh?

Building The Authentication System Part II

Let’s complete the rest of the authentication system while you are at it.

Open AppDelegate.m, and let’s make the application automatically log in the user if there are user credentials in the keychain.

Inside application:didFinishLaunchingWithOptions:, add the following code after the “Login with FatFractal by initiating connection with the server” comment:

// Step 1
[_ff loginWithUserName:username andPassword:password onComplete:^(NSError *theErr, id theObj, NSHTTPURLResponse *theResponse) {

    // Step 2
    if (theErr) {
        NSLog(@"Error trying to log in from AppDelegate: %@", [theErr localizedDescription]);
        // Probably keychain item is corrupted, reset the keychain and force user to sign up/ login again.
        // Better error handling can be done in a production application.
        [_keychainItem resetKeychainItem];
        return ;
    }

    // Step 3
    if (theObj) {
        NSLog(@"Login from AppDelegate using keychain successful!");
        [self userSuccessfullyAuthenticated];
    }
}];

In the code above, you login an existing user in the keychain by:

  1. Calling the loginWithUserName:AndPassword:onComplete method on your FatFractal instance, which will return with a response from the server.
  2. If there is an error, your keychain item is probably corrupted. Thus, you will clear it, and force the user to sign up and log in again when he next launches the application. In a production application, more robust error handling can be done.
  3. If there is no error, and the server returns you an FFUser object, you will send a message to the MainFeedViewController to inform it that user is successfully authenticated. This is so that you can know when to download the tweets and user information, as well as refresh the table view in the MainFeedViewController.

Build and run your application. If all goes well, in the log you should see the FFUser object being returned by the server and your log message that login from the app delegate using keychain was successful:

AppDelegateLoginAutomaticallyFirst

The Login View Controller

Registration works great, but the Login View Controller still doesn’t work, so let’s add the code for that next.

Open LoginViewController.m and add the following code to loginUserWithUsername:andPassword: after the “TODO” comment:

#pragma mark - Helper Methods
// Login with FatFractal, save to keychain and handleSuccessfulLogin if successful
// Step 1
[[FatFractal main] loginWithUserName:usernameText andPassword:passwordText onComplete:^(NSError *theErr, id theObj, NSHTTPURLResponse *theResponse) {

    // Step 2
    if (theErr) {
        NSLog(@"Error logging in from LoginViewController: %@", [theErr localizedDescription]);
        [MBProgressHUD hideHUDForView:self.view animated:YES];
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Your username and password could not be authenticated. Double check that you entered them correctly and try again." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert show];
    } else {

        // Step 3
        if (theObj) {
            // App is now in signed in state.
            [self saveUserCredentialsInKeychain];

            [MBProgressHUD hideHUDForView:self.view animated:YES];

            [self dismissViewControllerAnimated:YES completion:^{
                // Upon successful dismiss, handle login.
                [self handleSuccessfulLogin];
            }];

        }
    }
}];

In the code above, you login an user to your Fat Fractal backend by:

  1. Calling the loginWithUserName:AndPassword:onComplete method on your FatFractal instance, which will return with a response from the server.
  2. Handle the error, if any. Notice that you do not display the localized error, you will explain why in a short while.
  3. Check that the server actually returned you the FFUser object, save your credentials in the keychain and dismiss the modal view.

Now, why don’t you show the localized error message?

The reason is that the error message will reveal that the user is either using an incorrect password or user name that does not exist. This is bad in terms of security as you don’t want to make it easy for people to guess user names or passwords. Thus, you give a generic message that makes it hard to guess if the user name exists or the password is wrong. You can see it in the logs below:

IncorrectPasswordSecurityIssueOne
IncorrectPasswordSecurityIssueTwo

For now, you will not be using the Login View Controller, as you are automatically logged in when you sign up, and there is no log out function as of yet.

You will come to that in a while.