Chapter    9

The Login Feature

In the previous chapter, you learned how to sign up new users. In this chapter, you will learn how to implement the log in and log in with Facebook features.

Using Direct Log In

Similar to the sign-up feature, the user logs in with an e-mail and password, so you can use the same model (EMABUser), similar views (EMABEmailTextField, EMABPasswordTextField, and UITableView), and a “Log In” button.

Before requesting Parse to verify the information a user provides, you need to do some information check as you have done for the sign-up feature; for example, you need to check whether the e-mail is valid, the e-mail length meets the minimum length requirement, and the password length meets the minimum length requirement. You also need to set up some helper methods, one will repeat the e-mail verification method. For this reason, you move this method to EMABConstants.

The key Login API from Parse is + (void)logInWithUsernameInBackground:(NSString *)username password:(NSString *)password block:(PF_NULLABLE PFUserResultBlock)block.

Here is the implementation:

Using Facebook Login

It’s quite typical for an iOS e-commerce app to integrate Facebook Login for Apps. Although it can be challenging to manage two sets of users, the Facebook SDK can be used together with the Parse SDK, and is integrated with the PFUser class to make linking users to their Facebook identities easy.

This feature requires the ParseFacebookUtilities SDK, so you need to modify your Podfile as follows:

pod 'ParseFacebookUtilsV4','1.7.1'

Note  Facebook and Parse change their SDKs frequently. You may need to update the SDK version by the time you run this demo.

In this section, you learn how to implement Facebook Login with Parse, as well as how to obtain the user’s Facebook profile so you can save it for your own records.

Remember that you will need a Facebook Developer account for implementing this feature (see Chapter 4 for details).

For the model, use EMABUser (the same model as in Chapter 8); for the view, use the “Log In with Facebook” button (see Chapter 8). Since you need to talk to the Facebook API to authorize the user, you also need to obtain the user’s profile and save it to the Parse back end. I will introduce a UIActivityIndicator to indicate the ongoing process, and also disable some buttons in case users don’t want to wait until finished. The whole idea is to create a friendlier user experience.

Here is the header declaration:

#import "EMABDispatchViewController.h"
#import "EMABConstants.h"
#import "EMABUser.h"
#import <ParseFacebookUtils/PFFacebookUtils.h>
@interface EMABDispatchViewController ()
@property (nonatomic, weak) IBOutlet UIActivityIndicatorView *activityIndicatorView;
@end

When a user sees this screen, the c instance is hidden. Only when a user taps the “Log In with Facebook” button and a network request is made will it be visible. Once the network request is finished, the animation stops and the UIActivityIndicator instance is hidden.

@implementation EMABDispatchViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.activityIndicatorView.hidden = YES;

}

The helper method -(void)updateIndicator updates the UI. When making a Facebook API request, you want to disable the left bar button item to prevent the user from skipping the login process. You also use this method to show or hide the indicator.

-(void)updateIndicator:(BOOL)shouldEnable{
    self.navigationItem.leftBarButtonItem.enabled = !shouldEnable;
    (shouldEnable)?[self.activityIndicatorView startAnimating]:[self.activityIndicatorView stopAnimating];
    self.activityIndicatorView.hidden = !shouldEnable;

}

As you have done in previous chapters, use UIAlertView to show any error that might occur during the login process.

#pragma mark - helper
-(void)showError:(NSString *)message {
     [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error", @"Error") message:message delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"OK") otherButtonTitles:nil , nil] show];
}

Next, move to the Facebook login process. First, tell Facebook what information is obtained from the user, and ask permission from Facebook. In this case, you only need some basic information “user_about_me” such as the user’s e-mail, name, and gender. If you need more information, you need to log in to the developer.facebook.com portal to request additional permission.

Once again, Parse makes this process easy. You will use the PFFacebookUtils method logInWithPermissions:block to make this request.

When you make this request, you might get a success or an error message. If you get an error, you can simply let the user know there is something wrong and indicate what is wrong. You use the showError: method for this purpose. When you get a success, you will get a PFUser instance. But there is no Facebook user information associated with this PFUser instance; this requires an extra step. That’s why you need another helper method, obtainFacebookUserInfo:

-(IBAction)onFacebookLogin:(id)sender{
    // Set permissions required from the facebook user account
    NSArray *permissionsArray = @[@"user_about_me"];
    [self updateIndicator:YES];
    [PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
        if (error) {
                NSString *facebookError = [FBErrorUtility userMessageForError:error];
                [self showError:facebookError];
            } else {
                [self obtainFacebookUserInfo:user];
        }
        [self updateIndicator:NO];
    }];
}

To start requesting this user’s Facebook profile information, you use [FBRequest requestForMe] to create a FBRequest instance as shown in the following code. The next method is the FBRequest instance method, startWithCompletionHandler. This asynchronous callback will return either an error or the result you are looking for. Similarly, you can handle any error by showing it to the user; in the meantime, leave this screen, and return to the main app. You don’t want the user to get stuck here.

If you receive a successful result, you will map this dictionary result to the EMABUser instance properties. As mentioned before, the “user_about_me” permission will get you information about the user such as e-mail, name, gender, and profile image url. It’s easy to map the e-mail, name, and gender.

NSDictionary *userData = (NSDictionary *)result;
 [fbUser setEmail:userData[@"email"]];
 [fbUser setUsername:userData[@"email"]];
 [fbUser setName:userData[@"name"]];
 if (userData[@"gender"]) {
                [fbUser setGender:userData[@"gender"]];
 }

Mapping the image URL to PFFile as declared in EMABUser requires some additional work. First, you need to get the image URL in the correct format.

NSString *facebookID = userData[@"id"];
            NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=square&return_ssl_resources=1", facebookID]];

In this case, you use the user’s square profile picture. To transform from an image URL to PFFile, you can get the NSData from this URL, then use the [PFFile fileWithName:Data] class method to create a PFFile instance. The following code shows how to approach this problem:

if ([pictureURL absoluteString]) {
                dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
                dispatch_async(queue, ^{
                    NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[pictureURL absoluteString]]];
                    dispatch_async(dispatch_get_main_queue(), ^{
                        PFFile *iconFile = [PFFile fileWithName:@"avatar.jpg" data:imageData];
                        [iconFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
                            if (!error) {
                                fbUser.photo = iconFile;
                            }
                        }];

                    });
                });
            }

Finally, you save this EMABUser instance and dismiss the current screen:

[fbUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
                if (!error) {
                    [self dismissViewControllerAnimated:YES completion:nil];
                }
}];

Here is the complete implementation:

-(void)obtainFacebookUserInfo:(PFUser *)user{
    [self updateIndicator:YES];
    EMABUser *fbUser = (EMABUser *)user;
    FBRequest *request = [FBRequest requestForMe];
    [request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
        [self updateIndicator:NO];
        if (!error) {
            // Parse the data received

            NSString *facebookID = userData[@"id"];
            NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=square&return_ssl_resources=1", facebookID]];

            if ([pictureURL absoluteString]) {
                dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
                dispatch_async(queue, ^{
                    NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[pictureURL absoluteString]]];
                    dispatch_async(dispatch_get_main_queue(), ^{
                        PFFile *iconFile = [PFFile fileWithName:@"avatar.jpg" data:imageData];
                        [iconFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
                            if (!error) {
                                fbUser.photo = iconFile;
                            }
                        }];

                    });
                });
            }

            [fbUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
                if (!error) {
                    [self dismissViewControllerAnimated:YES completion:nil];
                }
            }];

        } else if ([[[[error userInfo] objectForKey:@"error"] objectForKey:@"type"]
                    isEqualToString: @"OAuthException"]) {
            [self dismissViewControllerAnimated:YES completion:^{
                [self showError:@"The facebook session was invalidated"];
            }];
        } else {
            [self dismissViewControllerAnimated:YES completion:^{
                [self showError:[error localizedDescription]];
            }];
        }
    }];
}

-(IBAction)onCancel:(id)sender{
    [self dismissViewControllerAnimated:YES completion:nil];
}

@end

Summary

In this chapter I explained how to log in a registered user while providing an intuitive and friendly user experience. I also covered how to implement Facebook Login with Apps, obtain a user’s Facebook profile, and then save the information to the Parse backend by incorporating the Parse ParseFacebookUtilsV4 SDK.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.138.104