Files
firebase-ios-sdk/Example/Auth/Sample/MainViewController.m
T

581 lines
22 KiB
Objective-C
Raw Normal View History

2017-05-15 12:27:07 -07:00
/*
2019-04-23 12:08:39 -07:00
* Copyright 2019 Google
2017-05-15 12:27:07 -07:00
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "MainViewController.h"
2019-05-02 16:27:05 -07:00
#import "MainViewController+App.h"
#import "MainViewController+Auth.h"
#import "MainViewController+AutoTests.h"
#import "MainViewController+Custom.h"
#import "MainViewController+Email.h"
#import "MainViewController+Facebook.h"
#import "MainViewController+GameCenter.h"
#import "MainViewController+Google.h"
#import "MainViewController+Internal.h"
#import "MainViewController+OAuth.h"
#import "MainViewController+OOB.h"
#import "MainViewController+Phone.h"
#import "MainViewController+User.h"
2017-05-15 12:27:07 -07:00
#import <objc/runtime.h>
2017-11-22 09:19:10 -08:00
#import <FirebaseCore/FIRApp.h>
#import <FirebaseCore/FIRAppInternal.h>
#import "AppManager.h"
2017-07-28 17:43:33 -07:00
#import "AuthCredentials.h"
2017-05-15 12:27:07 -07:00
#import "FIRAdditionalUserInfo.h"
#import "FIROAuthProvider.h"
#import "FIRPhoneAuthCredential.h"
#import "FIRPhoneAuthProvider.h"
2018-03-29 18:16:45 -07:00
#import "FIRAuthTokenResult.h"
2017-05-15 12:27:07 -07:00
#import "FirebaseAuth.h"
#import "FacebookAuthProvider.h"
#import "GoogleAuthProvider.h"
#import "SettingsViewController.h"
#import "StaticContentTableViewManager.h"
#import "UIViewController+Alerts.h"
#import "UserInfoViewController.h"
#import "UserTableViewCell.h"
2018-11-19 15:34:18 -08:00
#import "FIRAuth_Internal.h"
NS_ASSUME_NONNULL_BEGIN
2017-09-04 17:37:46 -07:00
2019-04-23 12:08:39 -07:00
static NSString *const kSectionTitleSettings = @"Settings";
2017-09-04 17:37:46 -07:00
2019-04-23 12:08:39 -07:00
static NSString *const kSectionTitleUserDetails = @"User Defaults";
2018-04-07 23:59:33 -07:00
static NSString *const kSwitchToInMemoryUserTitle = @"Switch to in memory user";
2018-04-03 17:19:51 -07:00
2017-10-10 10:32:15 -07:00
static NSString *const kNewOrExistingUserToggleTitle = @"New or Existing User Toggle";
2017-05-15 12:27:07 -07:00
typedef void (^FIRTokenCallback)(NSString *_Nullable token, NSError *_Nullable error);
@implementation MainViewController {
NSMutableString *_consoleString;
/** @var _userInMemory
@brief Acts like the "memory" function of a calculator. An operation allows sample app users
to assign this value based on @c FIRAuth.currentUser or clear this value.
*/
FIRUser *_userInMemory;
/** @var _useUserInMemory
@brief Instructs the application to use _userInMemory instead of @c FIRAuth.currentUser for
testing operations. This allows us to test if things still work with a user who is not
the @c FIRAuth.currentUser, and also allows us to test those things while
@c FIRAuth.currentUser remains nil (after a sign-out) and also when @c FIRAuth.currentUser
is non-nil (do to a subsequent sign-in.)
*/
BOOL _useUserInMemory;
}
- (id)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_actionCodeRequestType = ActionCodeRequestTypeInApp;
_actionCodeContinueURL = [NSURL URLWithString:KCONTINUE_URL];
2017-05-15 12:27:07 -07:00
_authStateDidChangeListeners = [NSMutableArray array];
_IDTokenDidChangeListeners = [NSMutableArray array];
_googleOAuthProvider = [FIROAuthProvider providerWithProviderID:FIRGoogleAuthProviderID];
_microsoftOAuthProvider = [FIROAuthProvider providerWithProviderID:@"microsoft.com"];
2017-05-15 12:27:07 -07:00
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(authStateChangedForAuth:)
name:FIRAuthStateDidChangeNotification
object:nil];
self.useStatusBarSpinner = YES;
}
return self;
}
- (void)viewDidLoad {
2018-11-30 17:30:22 -08:00
[super viewDidLoad];
2017-05-15 12:27:07 -07:00
// Give us a circle for the image view:
_userInfoTableViewCell.userInfoProfileURLImageView.layer.cornerRadius =
_userInfoTableViewCell.userInfoProfileURLImageView.frame.size.width / 2.0f;
_userInfoTableViewCell.userInfoProfileURLImageView.layer.masksToBounds = YES;
_userInMemoryInfoTableViewCell.userInfoProfileURLImageView.layer.cornerRadius =
_userInMemoryInfoTableViewCell.userInfoProfileURLImageView.frame.size.width / 2.0f;
_userInMemoryInfoTableViewCell.userInfoProfileURLImageView.layer.masksToBounds = YES;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
2017-05-15 12:27:07 -07:00
[self updateTable];
[self updateUserInfo];
}
2019-04-23 12:08:39 -07:00
#pragma mark - Public
2017-07-28 17:43:33 -07:00
- (BOOL)handleIncomingLinkWithURL:(NSURL *)URL {
// Parse the query portion of the incoming URL.
NSDictionary<NSString *, NSString *> *queryItems =
2019-04-23 12:08:39 -07:00
parseURL([NSURLComponents componentsWithString:URL.absoluteString].query);
2017-07-28 17:43:33 -07:00
// Check that all necessary query items are available.
2017-08-03 19:05:25 -07:00
NSString *actionCode = queryItems[@"oobCode"];
NSString *mode = queryItems[@"mode"];
if (!actionCode || !mode) {
2017-07-28 17:43:33 -07:00
return NO;
}
// Handle Password Reset action.
2017-08-03 19:05:25 -07:00
if ([mode isEqualToString:kPasswordResetAction]) {
2017-07-28 17:43:33 -07:00
[self showTextInputPromptWithMessage:@"New Password:"
completionBlock:^(BOOL userPressedOK, NSString *_Nullable newPassword) {
2019-04-23 12:08:39 -07:00
if (!userPressedOK || !newPassword.length) {
[UIPasteboard generalPasteboard].string = actionCode;
return;
}
[self showSpinner:^() {
[[AppManager auth] confirmPasswordResetWithCode:actionCode
newPassword:newPassword
completion:^(NSError *_Nullable error) {
2017-08-03 19:05:25 -07:00
[self hideSpinner:^{
if (error) {
[self logFailure:@"Password reset in app failed" error:error];
[self showMessagePrompt:error.localizedDescription];
return;
}
[self logSuccess:@"Password reset in app succeeded."];
[self showMessagePrompt:@"Password reset in app succeeded."];
}];
2017-07-28 17:43:33 -07:00
}];
}];
}];
2017-08-03 19:05:25 -07:00
return YES;
2017-07-28 17:43:33 -07:00
}
2017-08-03 19:05:25 -07:00
if ([mode isEqualToString:kVerifyEmailAction]) {
[self showMessagePromptWithTitle:@"Tap OK to verify email"
message:actionCode
2017-08-03 19:05:25 -07:00
showCancelButton:YES
completion:^(BOOL userPressedOK, NSString *_Nullable userInput) {
if (!userPressedOK) {
return;
}
[self showSpinner:^() {
[[AppManager auth] applyActionCode:actionCode completion:^(NSError *_Nullable error) {
[self hideSpinner:^{
if (error) {
[self logFailure:@"Verify email in app failed" error:error];
[self showMessagePrompt:error.localizedDescription];
return;
}
[self logSuccess:@"Verify email in app succeeded."];
[self showMessagePrompt:@"Verify email in app succeeded."];
}];
}];
}];
}];
return YES;
}
return NO;
2017-07-28 17:43:33 -07:00
}
2019-04-23 12:08:39 -07:00
static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) {
NSString *linkURL = [NSURLComponents componentsWithString:urlString].query;
NSArray<NSString *> *URLComponents = [linkURL componentsSeparatedByString:@"&"];
NSMutableDictionary<NSString *, NSString *> *queryItems =
[[NSMutableDictionary alloc] initWithCapacity:URLComponents.count];
for (NSString *component in URLComponents) {
NSRange equalRange = [component rangeOfString:@"="];
if (equalRange.location != NSNotFound) {
NSString *queryItemKey =
[[component substringToIndex:equalRange.location] stringByRemovingPercentEncoding];
NSString *queryItemValue =
[[component substringFromIndex:equalRange.location + 1] stringByRemovingPercentEncoding];
if (queryItemKey && queryItemValue) {
queryItems[queryItemKey] = queryItemValue;
}
}
}
return queryItems;
}
- (void)updateTable {
__weak typeof(self) weakSelf = self;
_tableViewManager.contents =
[StaticContentTableViewContent contentWithSections:@[
// User Defaults
[StaticContentTableViewSection sectionWithTitle:kSectionTitleUserDetails cells:@[
[StaticContentTableViewCell cellWithCustomCell:_userInfoTableViewCell action:^{
[weakSelf presentUserInfo];
}],
[StaticContentTableViewCell cellWithCustomCell:_userToUseCell],
[StaticContentTableViewCell cellWithCustomCell:_userInMemoryInfoTableViewCell action:^{
[weakSelf presentUserInMemoryInfo];
}],
]],
// Settings
[StaticContentTableViewSection sectionWithTitle:kSectionTitleSettings cells:@[
[StaticContentTableViewCell cellWithTitle:kSectionTitleSettings
action:^{ [weakSelf presentSettings]; }],
[StaticContentTableViewCell cellWithTitle:kNewOrExistingUserToggleTitle
value:_isNewUserToggleOn ? @"Enabled" : @"Disabled"
action:^{
_isNewUserToggleOn = !_isNewUserToggleOn;
[self updateTable]; }],
[StaticContentTableViewCell cellWithTitle:kSwitchToInMemoryUserTitle
action:^{ [weakSelf updateToSavedUser]; }],
]],
// Auth
[weakSelf authSection],
// Email Auth
[weakSelf emailAuthSection],
// Phone Auth
[weakSelf phoneAuthSection],
// Google Auth
[weakSelf googleAuthSection],
// Facebook Auth
[weakSelf facebookAuthSection],
// OAuth
[weakSelf oAuthSection],
// Custom Auth
[weakSelf customAuthSection],
// Game Center Auth
[weakSelf gameCenterAuthSection],
// User
[weakSelf userSection],
// App
[weakSelf appSection],
// OOB
[weakSelf oobSection],
// Auto Tests
[weakSelf autoTestsSection],
]];
}
#pragma mark - Internal
- (FIRUser *)user {
return _useUserInMemory ? _userInMemory : [AppManager auth].currentUser;
}
2017-05-15 12:27:07 -07:00
- (void)signInWithProvider:(nonnull id<AuthProvider>)provider callback:(void(^)(void))callback {
if (!provider) {
[self logFailedTest:@"A valid auth provider was not provided to the signInWithProvider."];
return;
}
[provider getAuthCredentialWithPresentingViewController:self
callback:^(FIRAuthCredential *credential,
NSError *error) {
if (!credential) {
[self logFailedTest:@"The test needs a valid credential to continue."];
return;
}
2019-04-23 12:08:39 -07:00
[[AppManager auth] signInWithCredential:credential
completion:^(FIRAuthDataResult *_Nullable result,
NSError *_Nullable error) {
2017-05-15 12:27:07 -07:00
if (error) {
[self logFailure:@"sign-in with provider failed" error:error];
[self logFailedTest:@"Sign-in should succeed"];
return;
} else {
[self logSuccess:@"sign-in with provider succeeded."];
callback();
}
}];
}];
}
2019-04-23 12:08:39 -07:00
- (FIRActionCodeSettings *)actionCodeSettings {
FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc] init];
actionCodeSettings.URL = self.actionCodeContinueURL;
actionCodeSettings.handleCodeInApp = self.actionCodeRequestType == ActionCodeRequestTypeInApp;
return actionCodeSettings;
2017-05-15 12:27:07 -07:00
}
- (void)reauthenticate:(id<AuthProvider>)authProvider retrieveData:(BOOL)retrieveData {
FIRUser *user = [self user];
if (!user) {
NSString *provider = @"Firebase";
if ([authProvider isKindOfClass:[GoogleAuthProvider class]]) {
provider = @"Google";
} else if ([authProvider isKindOfClass:[FacebookAuthProvider class]]) {
provider = @"Facebook";
}
NSString *title = @"Missing User";
NSString *message =
[NSString stringWithFormat:@"There is no signed-in %@ user.", provider];
[self showMessagePromptWithTitle:title message:message showCancelButton:NO completion:nil];
return;
}
[authProvider getAuthCredentialWithPresentingViewController:self
callback:^(FIRAuthCredential *credential,
NSError *error) {
if (credential) {
FIRAuthDataResultCallback completion = ^(FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error) {
if (error) {
[self logFailure:@"reauthenticate operation failed" error:error];
} else {
[self logSuccess:@"reauthenticate operation succeeded."];
}
if (authResult.additionalUserInfo) {
2017-09-26 13:17:41 -07:00
[self logSuccess:[self stringWithAdditionalUserInfo:authResult.additionalUserInfo]];
2017-05-15 12:27:07 -07:00
}
[self showTypicalUIForUserUpdateResultsWithTitle:@"Reauthenticate" error:error];
};
2019-04-23 12:08:39 -07:00
[user reauthenticateWithCredential:credential completion:completion];
2017-05-15 12:27:07 -07:00
}
}];
}
- (void)signinWithProvider:(id<AuthProvider>)authProvider retrieveData:(BOOL)retrieveData {
FIRAuth *auth = [AppManager auth];
2017-05-15 12:27:07 -07:00
if (!auth) {
return;
}
[authProvider getAuthCredentialWithPresentingViewController:self
callback:^(FIRAuthCredential *credential,
NSError *error) {
if (credential) {
FIRAuthDataResultCallback completion = ^(FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error) {
if (error) {
[self logFailure:@"sign-in with provider failed" error:error];
} else {
[self logSuccess:@"sign-in with provider succeeded."];
2017-10-06 11:59:53 -07:00
}
if (authResult.additionalUserInfo) {
[self logSuccess:[self stringWithAdditionalUserInfo:authResult.additionalUserInfo]];
2017-10-10 10:32:15 -07:00
if (_isNewUserToggleOn) {
NSString *newUserString = authResult.additionalUserInfo.isNewUser ?
@"New user" : @"Existing user";
[self showMessagePromptWithTitle:@"New or Existing"
message:newUserString
showCancelButton:NO
completion:nil];
}
2017-05-15 12:27:07 -07:00
}
[self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In" error:error];
};
2019-04-23 12:08:39 -07:00
[auth signInWithCredential:credential completion:completion];
2017-05-15 12:27:07 -07:00
}
}];
}
- (void)linkWithAuthProvider:(id<AuthProvider>)authProvider retrieveData:(BOOL)retrieveData {
FIRUser *user = [self user];
if (!user) {
return;
}
[authProvider getAuthCredentialWithPresentingViewController:self
callback:^(FIRAuthCredential *credential,
NSError *error) {
if (credential) {
FIRAuthDataResultCallback completion = ^(FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error) {
if (error) {
[self logFailure:@"link auth provider failed" error:error];
} else {
[self logSuccess:@"link auth provider succeeded."];
}
if (authResult.additionalUserInfo) {
2017-09-26 13:17:41 -07:00
[self logSuccess:[self stringWithAdditionalUserInfo:authResult.additionalUserInfo]];
2017-05-15 12:27:07 -07:00
}
if (retrieveData) {
[self showUIForAuthDataResultWithResult:authResult error:error];
} else {
[self showTypicalUIForUserUpdateResultsWithTitle:@"Link Account" error:error];
}
};
2019-04-23 12:08:39 -07:00
[user linkWithCredential:credential completion:completion];
2017-05-15 12:27:07 -07:00
}
}];
}
- (void)unlinkFromProvider:(NSString *)provider
2019-04-23 12:08:39 -07:00
completion:(nullable TestAutomationCallback)completion {
2017-05-15 12:27:07 -07:00
[[self user] unlinkFromProvider:provider
completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
if (error) {
[self logFailure:@"unlink auth provider failed" error:error];
if (completion) {
completion(error);
}
return;
}
[self logSuccess:@"unlink auth provider succeeded."];
if (completion) {
completion(nil);
2017-05-15 12:27:07 -07:00
}
2019-04-23 12:08:39 -07:00
[self showTypicalUIForUserUpdateResultsWithTitle:@"Unlink from Provider" error:error];
2017-05-15 12:27:07 -07:00
}];
}
2018-04-03 17:19:51 -07:00
- (void)updateToSavedUser {
if(![AppManager auth].currentUser) {
NSLog(@"You must be signed in to perform this action");
return;
}
if (!_userInMemory) {
2018-04-07 23:59:33 -07:00
[self showMessagePrompt:[NSString stringWithFormat:@"You need an in-memory user to perform this"
"action, use the M+ button to save a user to memory.", nil]];
2018-04-03 17:19:51 -07:00
return;
}
[[AppManager auth] updateCurrentUser:_userInMemory completion:^(NSError *_Nullable error) {
if (error) {
[self showMessagePrompt:
[NSString stringWithFormat:@"An error Occurred: %@", error.localizedDescription]];
return;
}
}];
}
2019-04-23 12:08:39 -07:00
#pragma mark - Private
2017-05-15 12:27:07 -07:00
2019-04-23 12:08:39 -07:00
- (void)presentSettings {
SettingsViewController *settingsViewController = [[SettingsViewController alloc]
initWithNibName:NSStringFromClass([SettingsViewController class])
bundle:nil];
[self showViewController:settingsViewController sender:self];
2017-05-15 12:27:07 -07:00
}
2019-04-23 12:08:39 -07:00
- (void)presentUserInfo {
UserInfoViewController *userInfoViewController =
[[UserInfoViewController alloc] initWithUser:[AppManager auth].currentUser];
[self showViewController:userInfoViewController sender:self];
}
2019-04-23 12:08:39 -07:00
- (void)presentUserInMemoryInfo {
UserInfoViewController *userInfoViewController =
[[UserInfoViewController alloc] initWithUser:_userInMemory];
[self showViewController:userInfoViewController sender:self];
}
2017-09-26 13:17:41 -07:00
- (NSString *)stringWithAdditionalUserInfo:(nullable FIRAdditionalUserInfo *)additionalUserInfo {
if (!additionalUserInfo) {
return @"(no additional user info)";
}
NSString *newUserString = additionalUserInfo.isNewUser ? @"new user" : @"existing user";
return [NSString stringWithFormat:@"%@: %@", newUserString, additionalUserInfo.profile];
}
2017-05-15 12:27:07 -07:00
- (void)showTypicalUIForUserUpdateResultsWithTitle:(NSString *)resultsTitle
2019-05-02 16:27:05 -07:00
error:(NSError * _Nullable)error {
2017-05-15 12:27:07 -07:00
if (error) {
NSString *message = [NSString stringWithFormat:@"%@ (%ld)\n%@",
error.domain,
(long)error.code,
error.localizedDescription];
if (error.code == FIRAuthErrorCodeAccountExistsWithDifferentCredential) {
NSString *errorEmail = error.userInfo[FIRAuthErrorUserInfoEmailKey];
resultsTitle = [NSString stringWithFormat:@"Existing email : %@", errorEmail];
}
[self showMessagePromptWithTitle:resultsTitle
message:message
showCancelButton:NO
completion:nil];
return;
}
[self updateUserInfo];
}
- (void)showUIForAuthDataResultWithResult:(FIRAuthDataResult *)result
2019-05-02 16:27:05 -07:00
error:(NSError * _Nullable)error {
2017-05-15 12:27:07 -07:00
NSString *errorMessage = [NSString stringWithFormat:@"%@ (%ld)\n%@",
error.domain ?: @"",
(long)error.code,
error.localizedDescription ?: @""];
[self showMessagePromptWithTitle:@"Error"
message:errorMessage
showCancelButton:NO
completion:^(BOOL userPressedOK,
NSString *_Nullable userInput) {
[self showMessagePromptWithTitle:@"Profile Info"
2017-09-26 13:17:41 -07:00
message:[self stringWithAdditionalUserInfo:result.additionalUserInfo]
2017-05-15 12:27:07 -07:00
showCancelButton:NO
completion:nil];
[self updateUserInfo];
}];
}
- (void)updateUserInfo {
[_userInfoTableViewCell updateContentsWithUser:[AppManager auth].currentUser];
2017-05-15 12:27:07 -07:00
[_userInMemoryInfoTableViewCell updateContentsWithUser:_userInMemory];
}
- (void)authStateChangedForAuth:(NSNotification *)notification {
[self updateUserInfo];
if (notification) {
[self log:[NSString stringWithFormat:
@"received FIRAuthStateDidChange notification on user '%@'.",
((FIRAuth *)notification.object).currentUser.uid]];
}
}
2019-04-23 12:08:39 -07:00
- (void)log:(NSString *)string {
dispatch_async(dispatch_get_main_queue(), ^{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSString *date = [dateFormatter stringFromDate:[NSDate date]];
if (!_consoleString) {
_consoleString = [NSMutableString string];
}
[_consoleString appendString:[NSString stringWithFormat:@"%@ %@\n", date, string]];
_consoleTextView.text = _consoleString;
CGRect targetRect = CGRectMake(0, _consoleTextView.contentSize.height - 1, 1, 1);
[_consoleTextView scrollRectToVisible:targetRect animated:YES];
});
}
- (void)logSuccess:(NSString *)string {
[self log:[NSString stringWithFormat:@"SUCCESS: %@", string]];
}
2019-05-02 16:27:05 -07:00
- (void)logFailure:(NSString *)string error:(NSError * _Nullable) error {
2019-04-23 12:08:39 -07:00
NSString *message =
[NSString stringWithFormat:@"FAILURE: %@ Error Description: %@.", string, error.description];
[self log:message];
}
- (void)logFailedTest:( NSString *_Nonnull )reason {
[self log:[NSString stringWithFormat:@"FAILIURE: TEST FAILED - %@", reason]];
}
#pragma mark - IBAction
- (IBAction)userToUseDidChange:(UISegmentedControl *)sender {
_useUserInMemory = (sender.selectedSegmentIndex == 1);
}
- (IBAction)memoryPlus {
_userInMemory = [AppManager auth].currentUser;
[self updateUserInfo];
}
- (IBAction)memoryClear {
_userInMemory = nil;
[self updateUserInfo];
}
2017-05-15 12:27:07 -07:00
- (IBAction)clearConsole:(id)sender {
[_consoleString appendString:@"\n\n"];
_consoleTextView.text = @"";
}
- (IBAction)copyConsole:(id)sender {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
pasteboard.string = _consoleString ?: @"";
}
@end
NS_ASSUME_NONNULL_END