Created
November 23, 2016 17:42
-
-
Save cjazz/5275790c270aa8c47c041c232fe7bc99 to your computer and use it in GitHub Desktop.
LoginFireBaseVC - Implements Logging auth for Firebase : Facebook/Twitter & set Email/PW. Also, captures email address & profile images
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// LoginVC.m | |
// | |
// Created by Adam Chin on 7/14/16. | |
// | |
// This implementation is an example of Login Methods to Firebase | |
// Google's MBaaS. It includes login auths to Facebook and Twitter | |
// as well as Email Logging. | |
// | |
// Client Requirements: | |
// * Loging / Auth via Facebook, Twitter & Email | |
// * Storing Users Email for future contact | |
// * Capturing Images from Facebook / Twitter for optionally setting | |
// the users profile image consistently or making a new one (outside of the scope of this file) | |
// Practical use & suggestions: | |
// 1) If you're not an iOS developer find one to explain | |
// 2) It is required to set up a Firebase account, and set up your project accordingly | |
// either manually or with CocoaPods | |
// 3) In the Firebase console, set up FaceBook, Twitter, and Email auth | |
// 4) Design a Login screen with appropriate UI IBOutlets | |
// | |
// If you would like to see this example working in an app, contact me | |
// and if I have available time I will help you. | |
// Note: This isn't 100% prod ready as a few spots need further error handling and clean up. | |
#import "LoginVC.h" | |
#import "MBProgressHUD.h" | |
#import "UIViewController+Alerts.h" | |
#import <FBSDKCoreKit/FBSDKCoreKit.h> | |
#import <FBSDKLoginKit/FBSDKLoginKit.h> | |
#import "IonIcons.h" | |
@import FirebaseAuth; | |
@import TwitterKit; | |
#import "MBProgressHUD.h" | |
#import "TwitterAPIBundle.h" | |
#define USERPW_AUTHENTICATION_TYPE @"Username-Password-Authentication" | |
#define HTTP_RESPONSE_ISSUE 202 | |
#define TWITTER_PROFILE_URL @"https://api.twitter.com/1.1/account/verify_credentials.json" | |
@interface LoginVC () <UITextFieldDelegate> | |
@property (weak, nonatomic) IBOutlet UIButton *signInButton; | |
@property (weak, nonatomic) IBOutlet UIButton *cancelButton; | |
@property(weak, nonatomic) IBOutlet UITextField *emailField; | |
@property(weak, nonatomic) IBOutlet UITextField *passwordField; | |
@property (nonatomic, weak) IBOutlet UIButton *facebookButton; | |
@property (nonatomic, weak) IBOutlet UIButton *twitterButton; | |
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *ActivityIndicator; | |
@property (strong, nonatomic) NSString *twitterEmail; | |
@property (nonatomic, weak) NSString *connectionType; | |
@end | |
@implementation LoginVC | |
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { | |
[self.view endEditing:YES]; | |
} | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
// Do any additional setup after loading the view. | |
self.ActivityIndicator.hidesWhenStopped = YES; | |
[self.emailField.layer addSublayer:[GlobalUI bottomBorderFor:self.emailField]]; | |
[self.passwordField.layer addSublayer:[GlobalUI bottomBorderFor:self.passwordField]]; | |
} | |
- (IBAction)cancel:(id)sender | |
{ | |
[self dismissViewControllerAnimated:YES completion:nil]; | |
} | |
-(void)viewWillAppear:(BOOL)animated | |
{ | |
[super viewWillAppear:animated]; | |
self.emailField.delegate = self; | |
self.passwordField.delegate = self; | |
self.signInButton.layer.cornerRadius = 5; | |
self.signInButton.clipsToBounds = YES; | |
self.cancelButton.layer.cornerRadius = 5; | |
self.cancelButton.clipsToBounds = YES; | |
self.facebookButton.layer.cornerRadius = 5; | |
self.facebookButton.clipsToBounds = YES; | |
self.twitterButton.layer.cornerRadius = 5; | |
self.twitterButton.clipsToBounds = YES; | |
} | |
- (BOOL)textFieldShouldReturn:(UITextField *)textField { | |
if (textField == self.emailField) | |
{ | |
[textField resignFirstResponder]; | |
[self.passwordField becomeFirstResponder]; | |
} else if (textField == self.passwordField) { | |
[self didTapEmailLogin:self]; | |
} | |
return YES; | |
} | |
- (IBAction)socialLogin:(id)sender { | |
[self.ActivityIndicator startAnimating]; | |
if (sender == self.facebookButton) | |
{ | |
if ([FBSDKAccessToken currentAccessToken]) // check for fb token | |
{ | |
[self.ActivityIndicator stopAnimating]; | |
UIAlertController *alert = [UIAlertController | |
alertControllerWithTitle:@"Facebook Login" | |
message:@"Already Logged in, no need to login again.." | |
preferredStyle:UIAlertControllerStyleAlert]; | |
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"Ok" | |
style:UIAlertActionStyleCancel | |
handler:^(UIAlertAction *action) { | |
}]; | |
[alert addAction:defaultAction]; | |
[self presentViewController:alert animated:YES completion:nil]; | |
} | |
else | |
{ | |
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init]; | |
[login logInWithReadPermissions:@[@"public_profile", @"email"] | |
fromViewController:self | |
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { | |
NSLog(@"Facebook Response Data: %@",result); | |
if (error) { | |
[self.ActivityIndicator stopAnimating]; | |
UIAlertController *alert = [UIAlertController | |
alertControllerWithTitle:@"Facebook" | |
message:@"Could not login to Facebook at this time, try again later.." | |
preferredStyle:UIAlertControllerStyleAlert]; | |
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"Ok" | |
style:UIAlertActionStyleCancel | |
handler:^(UIAlertAction *action) { | |
}]; | |
[alert addAction:defaultAction]; | |
[self presentViewController:alert animated:YES completion:nil]; | |
} else { | |
[self.ActivityIndicator stopAnimating]; | |
#ifdef DEBUG | |
NSLog(@"----------------------------------------------->"); | |
NSLog(@" FBSDKLoginManager: Login success"); | |
NSLog(@" Result: %@",result); | |
NSLog(@"----------------------------------------------->"); | |
#endif | |
if ([FBSDKAccessToken currentAccessToken]) // on | |
{ | |
// get facebook profile image with currentAccessToken.userID: | |
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=normal", | |
[FBSDKAccessToken currentAccessToken].userID]]; | |
NSData *data = [NSData dataWithContentsOfURL:url]; | |
if (data) | |
{ | |
NSString *fileName = @"faceBookProfile.jpg"; | |
NSFileManager *fm = [NSFileManager new]; | |
NSError *err = nil; | |
NSURL *docsURL = [fm URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&err]; | |
NSURL *jsonFolder = [docsURL URLByAppendingPathComponent:@"SocialProfileImages"]; | |
if ([fm createDirectoryAtURL:jsonFolder | |
withIntermediateDirectories:YES | |
attributes:nil | |
error:&err]) | |
{ | |
NSURL *fileURL = [jsonFolder URLByAppendingPathComponent:fileName]; | |
if ([data writeToURL:fileURL options:NSDataWritingAtomic error:&err]) | |
{ | |
#ifdef DEBUG | |
NSLog(@"----------------------------------------------->"); | |
NSLog(@" Saved Facebook Profile Pic"); | |
NSLog(@"----------------------------------------------->"); | |
#endif | |
} | |
else | |
{ | |
NSLog(@"file not saved :%@ error: %@",fileName, err); | |
} | |
} | |
} | |
FIRAuthCredential *credential = [FIRFacebookAuthProvider | |
credentialWithAccessToken:[FBSDKAccessToken currentAccessToken].tokenString]; | |
[[FIRAuth auth] signInWithCredential:credential | |
completion:^(FIRUser *user, NSError *error) { | |
if (error) { | |
NSLog(@"Error logging into Firebase: %@", error); | |
} else | |
{ | |
#ifdef DEBUG | |
NSLog(@"----------------------------------------------->"); | |
NSLog(@" FIRAuth Facebook SignInWithCredential: Login success"); | |
NSLog(@" Result: %@",user); | |
NSLog(@"----------------------------------------------->"); | |
#endif | |
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; | |
hud.mode = MBProgressHUDModeCustomView; | |
UIImage *image = [[UIImage imageNamed:@"Checkmark"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; | |
hud.customView = [[UIImageView alloc] initWithImage:image]; | |
hud.square = YES; | |
hud.label.text = NSLocalizedString(@"Success!", @"HUD done title"); | |
[hud hideAnimated:YES afterDelay:2.f]; | |
[self performSelector:@selector(dismissView) withObject:self afterDelay:2.0]; | |
} | |
}]; | |
} | |
} | |
}]; | |
} | |
} | |
else if (sender == self.twitterButton) | |
{ | |
self.connectionType = @"twitter"; | |
[self twitterStartLoginFlow]; | |
[self.ActivityIndicator stopAnimating]; | |
} | |
} | |
// MARK: twitter login methods | |
-(void)twitterStartLoginFlow | |
{ | |
TWTRLogInButton* logInButton = [TWTRLogInButton buttonWithLogInCompletion:^(TWTRSession* session, NSError* error) { | |
}]; | |
logInButton.loginMethods = TWTRLoginMethodWebBased; | |
[[Twitter sharedInstance] logInWithMethods:TWTRLoginMethodWebBased completion:^(TWTRSession *session, NSError *error) { | |
if (session) { | |
TWTRAPIClient *client = [TWTRAPIClient clientWithCurrentUser]; | |
NSURLRequest *request = [client URLRequestWithMethod:@"GET" | |
URL:TWITTER_PROFILE_URL | |
parameters:@{@"include_email": @"true", @"skip_status": @"true"} | |
error:nil]; | |
[client sendTwitterRequest:request completion:^(NSURLResponse *response, NSData *data, NSError *connectionError) { | |
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; | |
if ((long)[httpResponse statusCode] > HTTP_RESPONSE_ISSUE) | |
{ | |
UIAlertController *alert = [UIAlertController | |
alertControllerWithTitle:@"Twitter Login" | |
message:@"There's an issue with the Twitter service.." | |
preferredStyle:UIAlertControllerStyleAlert]; | |
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"Ok" | |
style:UIAlertActionStyleCancel | |
handler:^(UIAlertAction *action) { | |
}]; | |
[alert addAction:defaultAction]; | |
[self presentViewController:alert animated:YES completion:nil]; | |
return; | |
} | |
NSDictionary *responseData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; | |
if (responseData) { | |
self.twitterEmail = [responseData valueForKey:@"email"]; | |
if (self.twitterEmail.length) { | |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
[self twitterSignInToFirebaseAndLogEmail:session]; | |
[self getTwitterProfileImage:session]; | |
}); | |
[self.ActivityIndicator stopAnimating]; | |
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; | |
hud.mode = MBProgressHUDModeCustomView; | |
UIImage *image = [[UIImage imageNamed:@"Checkmark"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; | |
hud.customView = [[UIImageView alloc] initWithImage:image]; | |
hud.square = YES; | |
hud.label.text = NSLocalizedString(@"Success!", @"HUD done title"); | |
[hud hideAnimated:YES afterDelay:2.f]; | |
[self performSelector:@selector(dismissView) withObject:self afterDelay:2.f]; | |
} | |
} | |
}]; | |
} else [self.ActivityIndicator stopAnimating]; | |
}]; | |
} | |
-(void)twitterSignInToFirebaseAndLogEmail:(TWTRSession*)session | |
{ | |
if (session) | |
{ | |
__weak NSString *weakself = self.twitterEmail; | |
FIRAuthCredential *credential = [FIRTwitterAuthProvider credentialWithToken:session.authToken | |
secret:session.authTokenSecret]; | |
[[FIRAuth auth] signInWithCredential:credential | |
completion:^(FIRUser *user, NSError *error) { | |
if (error) { | |
// NSLog(@"Error logging into Twitter: %@", error); | |
} | |
else | |
{ | |
[[FIRAuth auth].currentUser updateEmail:weakself | |
completion:^(NSError * _Nullable error) { | |
if (error) { | |
} | |
else{ | |
#ifdef DEBUG | |
NSLog(@"----------------------------------------------->"); | |
NSLog(@" FIRAuth Twitter Added email to Firebase profile:"); | |
NSLog(@" Result: %@",user); | |
NSLog(@"----------------------------------------------->"); | |
#endif | |
} | |
}]; | |
} | |
}]; | |
} else [self.ActivityIndicator stopAnimating]; | |
} | |
-(void)getTwitterProfileImage:(TWTRSession*)session | |
{ | |
if (session) { | |
TWTRAPIClient *client = [TWTRAPIClient clientWithCurrentUser]; | |
NSString *userID = [Twitter sharedInstance].sessionStore.session.userID; | |
[client loadUserWithID:userID completion:^(TWTRUser * _Nullable user, NSError * _Nullable error) { | |
if (user.profileImageLargeURL.length) | |
{ | |
NSString *stringURL = user.profileImageLargeURL; | |
NSURL *url = [NSURL URLWithString:stringURL]; | |
NSData *urlData = [NSData dataWithContentsOfURL:url]; | |
if (urlData) | |
{ | |
NSString *fileName = @"twitterProfile.jpg"; | |
NSFileManager *fm = [NSFileManager new]; | |
NSError *err = nil; | |
NSURL *docsURL = [fm URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&err]; | |
NSURL *jsonFolder = [docsURL URLByAppendingPathComponent:@"SocialProfileImages"]; | |
if ([fm createDirectoryAtURL:jsonFolder | |
withIntermediateDirectories:YES | |
attributes:nil | |
error:&err]) | |
{ | |
NSURL *fileURL = [jsonFolder URLByAppendingPathComponent:fileName]; | |
if ([urlData writeToURL:fileURL options:NSDataWritingAtomic error:&err]) | |
{ | |
#ifdef DEBUG | |
NSLog(@"----------------------------------------------->"); | |
NSLog(@" Saved Twitter Profile Image:"); | |
NSLog(@"----------------------------------------------->"); | |
#endif | |
} | |
else | |
{ | |
NSLog(@"file not saved :%@ error: %@",fileName, err); | |
} | |
} | |
} | |
} | |
}]; | |
} else [self.ActivityIndicator stopAnimating]; | |
} | |
// MARK: email login methods | |
- (IBAction)didTapEmailLogin:(id)sender { | |
[self.ActivityIndicator startAnimating]; | |
NSString *email = self.emailField.text; | |
NSString *password = self.passwordField.text; | |
[[FIRAuth auth] signInWithEmail:email | |
password:password | |
completion:^(FIRUser * _Nullable user, NSError * _Nullable error) { | |
if (error) { | |
[self.ActivityIndicator stopAnimating]; | |
} | |
else { | |
#ifdef DEBUG | |
NSLog(@"----------------------------------------------->"); | |
NSLog(@" FIRAuth Sign in with Email"); | |
NSLog(@" Success with user: %@",user); | |
NSLog(@"----------------------------------------------->"); | |
#endif | |
[self.ActivityIndicator stopAnimating]; | |
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; | |
hud.mode = MBProgressHUDModeCustomView; | |
UIImage *image = [[UIImage imageNamed:@"Checkmark"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; | |
hud.customView = [[UIImageView alloc] initWithImage:image]; | |
hud.square = YES; | |
hud.label.text = NSLocalizedString(@"Success!", @"HUD done title"); | |
[hud hideAnimated:YES afterDelay:2.f]; | |
[self performSelector:@selector(dismissView) withObject:self afterDelay:2.f]; | |
} | |
}]; | |
} | |
- (IBAction)didRequestPasswordReset:(id)sender { | |
[self showTextInputPromptWithMessage:@"Enter Email for password reset:" | |
completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { | |
if (!userPressedOK || !userInput.length) { | |
return; | |
} | |
[self showSpinner:^{ | |
[[FIRAuth auth] | |
sendPasswordResetWithEmail:userInput | |
completion:^(NSError *_Nullable error) { | |
// [START_EXCLUDE] | |
[self hideSpinner:^{ | |
if (error) { | |
[self | |
showMessagePrompt:error | |
.localizedDescription]; | |
return; | |
} | |
[self showMessagePrompt:@"Sent"]; | |
}]; | |
}]; | |
}]; | |
}]; | |
} | |
-(void)dismissView | |
{ | |
[self dismissViewControllerAnimated:YES completion:nil]; | |
} | |
- (void)didReceiveMemoryWarning | |
{ | |
[super didReceiveMemoryWarning]; | |
// Dispose of any resources that can be recreated. | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment