adding pods method of package managing
This commit is contained in:
Generated
Executable
+304
@@ -0,0 +1,304 @@
|
||||
//
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// 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 "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIConfirmEmailViewController.h"
|
||||
|
||||
#import <FirebaseAuth/FirebaseAuth.h>
|
||||
|
||||
#import <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailAuth.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuth_Internal.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuthStrings.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordSignInViewController.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordSignUpViewController.h"
|
||||
|
||||
/** @var kCellReuseIdentifier
|
||||
@brief The reuse identifier for table view cell.
|
||||
*/
|
||||
static NSString *const kCellReuseIdentifier = @"cellReuseIdentifier";
|
||||
|
||||
/** @var kAppIDCodingKey
|
||||
@brief The key used to encode the app ID for NSCoding.
|
||||
*/
|
||||
static NSString *const kAppIDCodingKey = @"appID";
|
||||
|
||||
/** @var kAuthUICodingKey
|
||||
@brief The key used to encode @c FUIAuth instance for NSCoding.
|
||||
*/
|
||||
static NSString *const kAuthUICodingKey = @"authUI";
|
||||
|
||||
/** @var kEmailCellAccessibilityID
|
||||
@brief The Accessibility Identifier for the @c email sign in cell.
|
||||
*/
|
||||
static NSString *const kEmailCellAccessibilityID = @"EmailCellAccessibilityID";
|
||||
|
||||
/** @var kNextButtonAccessibilityID
|
||||
@brief The Accessibility Identifier for the @c next button.
|
||||
*/
|
||||
static NSString *const kNextButtonAccessibilityID = @"NextButtonAccessibilityID";
|
||||
|
||||
@interface FUIConfirmEmailViewController () <UITableViewDataSource, UITextFieldDelegate>
|
||||
|
||||
/** @property emailField
|
||||
@brief The @c UITextField that user enters email address into.
|
||||
*/
|
||||
@property (nonatomic) UITextField *emailField;
|
||||
|
||||
/** @property tableView
|
||||
@brief The @c UITableView used to store all UI elements.
|
||||
*/
|
||||
@property (nonatomic, weak) IBOutlet UITableView *tableView;
|
||||
|
||||
/** @property termsOfServiceView
|
||||
@brief The @c Text view which displays Terms of Service.
|
||||
*/
|
||||
@property (nonatomic, weak) IBOutlet FUIPrivacyAndTermsOfServiceView *termsOfServiceView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUIConfirmEmailViewController
|
||||
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI {
|
||||
return [self initWithNibName:NSStringFromClass([self class])
|
||||
bundle:[FUIEmailAuth bundle]
|
||||
authUI:authUI];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(NSString *)nibNameOrNil
|
||||
bundle:(NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI {
|
||||
self = [super initWithNibName:nibNameOrNil
|
||||
bundle:nibBundleOrNil
|
||||
authUI:authUI];
|
||||
if (self) {
|
||||
self.title = FUILocalizedString(kStr_ConfirmEmail);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
UIBarButtonItem *nextButtonItem =
|
||||
[FUIAuthBaseViewController barItemWithTitle:FUILocalizedString(kStr_Next)
|
||||
target:self
|
||||
action:@selector(next)];
|
||||
nextButtonItem.accessibilityIdentifier = kNextButtonAccessibilityID;
|
||||
self.navigationItem.rightBarButtonItem = nextButtonItem;
|
||||
self.termsOfServiceView.authUI = self.authUI;
|
||||
[self.termsOfServiceView useFullMessage];
|
||||
|
||||
[self enableDynamicCellHeightForTableView:self.tableView];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (self.navigationController.viewControllers.firstObject == self) {
|
||||
if (!self.authUI.shouldHideCancelButton) {
|
||||
UIBarButtonItem *cancelBarButton =
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
|
||||
target:self
|
||||
action:@selector(cancelAuthorization)];
|
||||
self.navigationItem.leftBarButtonItem = cancelBarButton;
|
||||
}
|
||||
self.navigationItem.backBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithTitle:FUILocalizedString(kStr_Back)
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:nil
|
||||
action:nil];
|
||||
|
||||
if (@available(iOS 13, *)) {
|
||||
if (!self.authUI.isInteractiveDismissEnabled) {
|
||||
self.modalInPresentation = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)next {
|
||||
[self onNext:self.emailField.text];
|
||||
}
|
||||
|
||||
- (void)onNext:(NSString *)emailText {
|
||||
FUIEmailAuth *emailAuth = [self.authUI providerWithID:FIREmailAuthProviderID];
|
||||
|
||||
if (![[self class] isValidEmail:emailText]) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)];
|
||||
return;
|
||||
}
|
||||
|
||||
[self incrementActivity];
|
||||
FIRAuthCredential *credential =
|
||||
[FIREmailAuthProvider credentialWithEmail:emailText link:emailAuth.emailLink];
|
||||
|
||||
void (^completeSignInBlock)(FIRAuthDataResult *, NSError *) = ^(FIRAuthDataResult *authResult,
|
||||
NSError *error) {
|
||||
[self decrementActivity];
|
||||
|
||||
if (error) {
|
||||
switch (error.code) {
|
||||
case FIRAuthErrorCodeWrongPassword:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_WrongPasswordError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserNotFound:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_UserNotFoundError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserDisabled:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_AccountDisabledError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeTooManyRequests:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_SignInTooManyTimesError)];
|
||||
return;
|
||||
default:
|
||||
[self showAlertWithMessage:error.description];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[[self class] showAlertWithTitle:FUILocalizedString(kStr_SignedIn)
|
||||
message:nil
|
||||
actionTitle:nil
|
||||
actionHandler:nil
|
||||
dismissTitle:@"OK"
|
||||
dismissHandler:^{
|
||||
[self.navigationController dismissViewControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:authResult URL:nil error:error];
|
||||
}];
|
||||
}
|
||||
presentingViewController:self];
|
||||
};
|
||||
|
||||
[self.auth signInWithCredential:credential completion:completeSignInBlock];
|
||||
}
|
||||
|
||||
- (void)textFieldDidChange {
|
||||
[self didChangeEmail:self.emailField.text];
|
||||
}
|
||||
|
||||
- (void)didChangeEmail:(NSString *)emailText {
|
||||
self.navigationItem.rightBarButtonItem.enabled = (emailText.length > 0);
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView
|
||||
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FUIAuthTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
if (!cell) {
|
||||
UINib *cellNib = [UINib nibWithNibName:NSStringFromClass([FUIAuthTableViewCell class])
|
||||
bundle:[FUIAuthUtils authUIBundle]];
|
||||
[tableView registerNib:cellNib forCellReuseIdentifier:kCellReuseIdentifier];
|
||||
cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
}
|
||||
cell.label.text = FUILocalizedString(kStr_Email);
|
||||
cell.textField.placeholder = FUILocalizedString(kStr_ConfirmEmail);
|
||||
cell.textField.delegate = self;
|
||||
cell.accessibilityIdentifier = kEmailCellAccessibilityID;
|
||||
self.emailField = cell.textField;
|
||||
cell.textField.secureTextEntry = NO;
|
||||
cell.textField.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
cell.textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
cell.textField.returnKeyType = UIReturnKeyNext;
|
||||
cell.textField.keyboardType = UIKeyboardTypeEmailAddress;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
cell.textField.textContentType = UITextContentTypeUsername;
|
||||
}
|
||||
[cell.textField addTarget:self
|
||||
action:@selector(textFieldDidChange)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
[self didChangeEmail:self.emailField.text];
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (nullable id<FUIAuthProvider>)bestProviderFromProviderIDs:(NSArray<NSString *> *)providerIDs {
|
||||
NSArray<id<FUIAuthProvider>> *providers = self.authUI.providers;
|
||||
for (NSString *providerID in providerIDs) {
|
||||
for (id<FUIAuthProvider> provider in providers) {
|
||||
if ([providerID isEqual:provider.providerID]) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||
if (textField == self.emailField) {
|
||||
[self onNext:self.emailField.text];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Utilities
|
||||
|
||||
/** @fn signInWithProvider:email:
|
||||
@brief Actually kicks off sign in with the provider.
|
||||
@param provider The identity provider to sign in with.
|
||||
@param email The email address of the user.
|
||||
*/
|
||||
- (void)signInWithProvider:(id<FUIAuthProvider>)provider email:(NSString *)email {
|
||||
[self incrementActivity];
|
||||
|
||||
// Sign out first to make sure sign in starts with a clean state.
|
||||
[provider signOut];
|
||||
[provider signInWithDefaultValue:email
|
||||
presentingViewController:self
|
||||
completion:^(FIRAuthCredential * _Nullable credential,
|
||||
NSError * _Nullable error,
|
||||
FIRAuthResultCallback _Nullable result,
|
||||
NSDictionary<NSString *,id> * _Nullable userInfo) {
|
||||
if (error) {
|
||||
[self decrementActivity];
|
||||
if (result) {
|
||||
result(nil, error);
|
||||
}
|
||||
|
||||
[self dismissNavigationControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:nil URL:nil error:error];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
[self.auth signInWithCredential:credential
|
||||
completion:^(FIRAuthDataResult *_Nullable authResult,
|
||||
NSError *_Nullable error) {
|
||||
[self decrementActivity];
|
||||
if (result) {
|
||||
result(authResult.user, error);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:nil URL:nil error:error];
|
||||
} else {
|
||||
[self dismissNavigationControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:authResult URL:nil error:error];
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,868 @@
|
||||
//
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// 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 "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailAuth.h"
|
||||
|
||||
#import <FirebaseCore/FIRApp.h>
|
||||
#import <FirebaseCore/FIROptions.h>
|
||||
#import <FirebaseAuth/FirebaseAuth.h>
|
||||
#import <GoogleUtilities/GULUserDefaults.h>
|
||||
|
||||
#import <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIConfirmEmailViewController.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuthStrings.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuth_Internal.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailEntryViewController.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIPasswordSignInViewController_Internal.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordVerificationViewController.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordSignInViewController.h"
|
||||
|
||||
/** @var kErrorUserInfoEmailKey
|
||||
@brief The key for the email address in the userinfo dictionary of a sign in error.
|
||||
*/
|
||||
static NSString *const kErrorUserInfoEmailKey = @"FIRAuthErrorUserInfoEmailKey";
|
||||
|
||||
/** @var kEmailButtonAccessibilityID
|
||||
@brief The Accessibility Identifier for the @c email sign in button.
|
||||
*/
|
||||
static NSString *const kEmailButtonAccessibilityID = @"EmailButtonAccessibilityID";
|
||||
|
||||
/** @var kEmailLinkSignInEmailKey
|
||||
@brief The key of the email which request email link sign in.
|
||||
*/
|
||||
static NSString *const kEmailLinkSignInEmailKey = @"FIRAuthEmailLinkSignInEmail";
|
||||
|
||||
/** @var kEmailLinkSignInLinkingCredentialKey
|
||||
@brief The key of the auth credential to be linked.
|
||||
*/
|
||||
static NSString *const kEmailLinkSignInLinkingCredentialKey = @"FIRAuthEmailLinkSignInLinkingCredential";
|
||||
|
||||
@interface FUIEmailAuth () <FUIEmailAuthProvider>
|
||||
/** @property authUI.
|
||||
@brief The @c FUIAuth instance of the application.
|
||||
*/
|
||||
@property(nonatomic, strong, readonly) FUIAuth *authUI;
|
||||
|
||||
/** @property pendingSignInCallback.
|
||||
@brief The callback which should be invoked when the sign in flow completes (or is cancelled.)
|
||||
*/
|
||||
@property(nonatomic, copy, readwrite) FUIAuthProviderSignInCompletionBlock pendingSignInCallback;
|
||||
|
||||
/** @property presentingViewController
|
||||
@brief The presenting view controller for interactive sign-in.
|
||||
*/
|
||||
@property(nonatomic, strong) UIViewController *presentingViewController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUIEmailAuth
|
||||
|
||||
+ (NSBundle *)bundle {
|
||||
return [FUIAuthUtils bundleNamed:FUIEmailAuthBundleName
|
||||
inFrameworkBundle:[NSBundle bundleForClass:[self class]]];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initAuthAuthUI:[FUIAuth defaultAuthUI]
|
||||
signInMethod:FIREmailPasswordAuthSignInMethod
|
||||
forceSameDevice:NO
|
||||
allowNewEmailAccounts:YES
|
||||
requireDisplayName:YES
|
||||
actionCodeSetting:[[FIRActionCodeSettings alloc] init]];
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initAuthAuthUI:(FUIAuth *)authUI
|
||||
signInMethod:(NSString *)signInMethod
|
||||
forceSameDevice:(BOOL)forceSameDevice
|
||||
allowNewEmailAccounts:(BOOL)allowNewEmailAccounts
|
||||
actionCodeSetting:(FIRActionCodeSettings *)actionCodeSettings {
|
||||
return [self initAuthAuthUI:authUI
|
||||
signInMethod:signInMethod
|
||||
forceSameDevice:forceSameDevice
|
||||
allowNewEmailAccounts:allowNewEmailAccounts
|
||||
requireDisplayName:YES
|
||||
actionCodeSetting:actionCodeSettings];
|
||||
}
|
||||
|
||||
- (instancetype)initAuthAuthUI:(FUIAuth *)authUI
|
||||
signInMethod:(NSString *)signInMethod
|
||||
forceSameDevice:(BOOL)forceSameDevice
|
||||
allowNewEmailAccounts:(BOOL)allowNewEmailAccounts
|
||||
requireDisplayName:(BOOL)requireDisplayName
|
||||
actionCodeSetting:(FIRActionCodeSettings *)actionCodeSettings {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_authUI = authUI;
|
||||
_authUI.emailAuthProvider = self;
|
||||
_signInMethod = signInMethod;
|
||||
_forceSameDevice = forceSameDevice;
|
||||
_allowNewEmailAccounts = allowNewEmailAccounts;
|
||||
_requireDisplayName = requireDisplayName;
|
||||
_actionCodeSettings = actionCodeSettings;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - FUIAuthProvider
|
||||
|
||||
- (nullable NSString *)providerID {
|
||||
return FIREmailAuthProviderID;
|
||||
}
|
||||
|
||||
/** @fn accessToken:
|
||||
@brief Email Auth token is matched by FirebaseUI User Access Token
|
||||
*/
|
||||
- (nullable NSString *)accessToken {
|
||||
return nil;
|
||||
}
|
||||
|
||||
/** @fn idToken:
|
||||
@brief Email Auth Token Secret is matched by FirebaseUI User Id Token
|
||||
*/
|
||||
- (nullable NSString *)idToken {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)shortName {
|
||||
return @"Email";
|
||||
}
|
||||
|
||||
- (NSString *)signInLabel {
|
||||
return FUILocalizedString(kStr_SignInWithEmail);
|
||||
}
|
||||
|
||||
- (UIImage *)icon {
|
||||
return [FUIAuthUtils imageNamed:@"ic_email" fromBundle:[FUIEmailAuth bundle]];
|
||||
}
|
||||
|
||||
- (UIColor *)buttonBackgroundColor {
|
||||
return [UIColor colorWithRed:208.f/255.f green:2.f/255.f blue:27.f/255.f alpha:1.0];
|
||||
}
|
||||
|
||||
- (UIColor *)buttonTextColor {
|
||||
return [UIColor whiteColor];
|
||||
}
|
||||
|
||||
- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController {
|
||||
[self signInWithPresentingViewController:presentingViewController
|
||||
email:nil];
|
||||
}
|
||||
|
||||
- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
|
||||
email:(nullable NSString *)email {
|
||||
[self.authUI signInWithProviderUI:self
|
||||
presentingViewController:presentingViewController
|
||||
defaultValue:email];
|
||||
}
|
||||
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
|
||||
- (void)signInWithEmail:(nullable NSString *)email
|
||||
presentingViewController:(nullable UIViewController *)presentingViewController
|
||||
completion:(nullable FUIAuthProviderSignInCompletionBlock)completion {
|
||||
[self signInWithDefaultValue:email
|
||||
presentingViewController:presentingViewController
|
||||
completion:completion];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)signInWithDefaultValue:(nullable NSString *)defaultValue
|
||||
presentingViewController:(nullable UIViewController *)presentingViewController
|
||||
completion:(nullable FUIAuthProviderSignInCompletionBlock)completion {
|
||||
self.presentingViewController = presentingViewController;
|
||||
|
||||
self.pendingSignInCallback = completion;
|
||||
id<FUIAuthDelegate> delegate = self.authUI.delegate;
|
||||
UIViewController *controller;
|
||||
if (self.allowNewEmailAccounts) {
|
||||
if ([delegate respondsToSelector:@selector(emailEntryViewControllerForAuthUI:)]) {
|
||||
controller = [delegate emailEntryViewControllerForAuthUI:self.authUI];
|
||||
} else {
|
||||
controller = [[FUIEmailEntryViewController alloc] initWithAuthUI:self.authUI];
|
||||
}
|
||||
} else {
|
||||
if ([delegate respondsToSelector:@selector(passwordSignInViewControllerForAuthUI:email:)]) {
|
||||
controller = [delegate passwordSignInViewControllerForAuthUI:self.authUI
|
||||
email:defaultValue];
|
||||
} else {
|
||||
controller = [[FUIPasswordSignInViewController alloc] initWithAuthUI:self.authUI
|
||||
email:defaultValue];
|
||||
}
|
||||
}
|
||||
|
||||
if ([presentingViewController isKindOfClass:[FUIAuthBaseViewController class]]) {
|
||||
FUIAuthBaseViewController *authController =
|
||||
(FUIAuthBaseViewController *)presentingViewController;
|
||||
[authController pushViewController:controller];
|
||||
} else {
|
||||
UINavigationController *navigationController =
|
||||
[[UINavigationController alloc] initWithRootViewController:controller];
|
||||
[presentingViewController presentViewController:navigationController
|
||||
animated:YES
|
||||
completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)signOut {
|
||||
return;
|
||||
}
|
||||
|
||||
- (BOOL)handleOpenURL:(NSURL *)URL sourceApplication:(nullable NSString *)sourceApplication {
|
||||
self.emailLink = URL.absoluteString;
|
||||
|
||||
// Retrieve continueUrl from URL
|
||||
NSURLComponents *urlComponents = [NSURLComponents componentsWithString:URL.absoluteString];
|
||||
NSString *continueURLString;
|
||||
for (NSURLQueryItem *queryItem in urlComponents.queryItems) {
|
||||
if ([queryItem.name isEqualToString:@"continueUrl"]) {
|
||||
continueURLString = queryItem.value;
|
||||
}
|
||||
}
|
||||
if (!continueURLString) {
|
||||
NSLog(@"FUIEmailAuth unable to handle url without continue URL: %@", URL);
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Retrieve url parameters from continueUrl
|
||||
NSMutableDictionary *urlParameterDict= [NSMutableDictionary dictionary];
|
||||
NSURLComponents *continueURLComponents = [NSURLComponents componentsWithString:continueURLString];
|
||||
for (NSURLQueryItem *queryItem in continueURLComponents.queryItems) {
|
||||
urlParameterDict[queryItem.name] = queryItem.value;
|
||||
}
|
||||
// Retrieve parameters from local storage
|
||||
NSMutableDictionary *localParameterDict = [NSMutableDictionary dictionary];
|
||||
localParameterDict[kEmailLinkSignInEmailKey] = [GULUserDefaults.standardUserDefaults
|
||||
stringForKey:kEmailLinkSignInEmailKey];
|
||||
localParameterDict[@"ui_sid"] = [GULUserDefaults.standardUserDefaults stringForKey:@"ui_sid"];
|
||||
|
||||
// Handling flows
|
||||
NSString *urlSessionID = urlParameterDict[@"ui_sid"];
|
||||
NSString *localSessionID = localParameterDict[@"ui_sid"];
|
||||
BOOL sameDevice = urlSessionID && localSessionID && [urlSessionID isEqualToString:localSessionID];
|
||||
|
||||
if (sameDevice) {
|
||||
// Same device
|
||||
if (urlParameterDict[@"ui_pid"]) {
|
||||
// Unverified provider linking
|
||||
NSError *error = nil;
|
||||
[self handleUnverifiedProviderLinking:urlParameterDict[@"ui_pid"]
|
||||
email:localParameterDict[kEmailLinkSignInEmailKey]
|
||||
error:&error];
|
||||
if (error != nil) {
|
||||
NSLog(@"Error verifying provider linking: %@", error);
|
||||
return NO;
|
||||
}
|
||||
} else if (urlParameterDict[@"ui_auid"]) {
|
||||
// Anonymous upgrade
|
||||
[self handleAnonymousUpgrade:urlParameterDict[@"ui_auid"]
|
||||
email:localParameterDict[kEmailLinkSignInEmailKey]];
|
||||
} else {
|
||||
// Normal email link sign in
|
||||
[self handleEmaiLinkSignIn:localParameterDict[kEmailLinkSignInEmailKey]];
|
||||
}
|
||||
} else {
|
||||
// Different device
|
||||
if ([urlParameterDict[@"ui_sd"] isEqualToString:@"1"]) {
|
||||
// Force same device enabled
|
||||
[self handleDifferentDevice];
|
||||
} else {
|
||||
// Force same device not enabled
|
||||
[self handleConfirmEmail];
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)handleUnverifiedProviderLinking:(NSString *)providerID
|
||||
email:(NSString *)email
|
||||
error:(NSError **)error {
|
||||
if ([providerID isEqualToString:FIRFacebookAuthProviderID]) {
|
||||
NSData *unverifiedProviderCredentialData = [GULUserDefaults.standardUserDefaults
|
||||
objectForKey:kEmailLinkSignInLinkingCredentialKey];
|
||||
FIRAuthCredential *unverifiedProviderCredential;
|
||||
|
||||
// TODO:
|
||||
// The replacement method for `unarchiveObjectWithData:` requires NSSecureCoding, which
|
||||
// FIRAuthCredential does not yet conform to.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
unverifiedProviderCredential =
|
||||
[NSKeyedUnarchiver unarchiveObjectWithData:unverifiedProviderCredentialData];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
FIRAuthCredential *emailLinkCredential =
|
||||
[FIREmailAuthProvider credentialWithEmail:email link:self.emailLink];
|
||||
|
||||
void (^completeSignInBlock)(FIRAuthDataResult *, NSError *) = ^(FIRAuthDataResult *authResult,
|
||||
NSError *error) {
|
||||
if (error) {
|
||||
switch (error.code) {
|
||||
case FIRAuthErrorCodeWrongPassword:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_WrongPasswordError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserNotFound:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_UserNotFoundError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserDisabled:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_AccountDisabledError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeTooManyRequests:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_SignInTooManyTimesError)];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void (^dismissHandler)(void) = ^() {
|
||||
UINavigationController *authViewController = [self.authUI authViewController];
|
||||
if (!(authViewController.isViewLoaded && authViewController.view.window)) {
|
||||
[authViewController.navigationController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:authResult URL:nil error:error];
|
||||
};
|
||||
|
||||
[FUIAuthBaseViewController showAlertWithTitle:FUILocalizedString(kStr_SignedIn)
|
||||
message:nil
|
||||
actionTitle:nil
|
||||
actionHandler:nil
|
||||
dismissTitle:@"OK"
|
||||
dismissHandler:dismissHandler
|
||||
presentingViewController:nil];
|
||||
};
|
||||
|
||||
[self.authUI.auth signInWithCredential:emailLinkCredential
|
||||
completion:^(FIRAuthDataResult * _Nullable authResult,
|
||||
NSError * _Nullable error) {
|
||||
if (error) {
|
||||
[FUIAuthBaseViewController showAlertWithMessage:error.description];
|
||||
return;
|
||||
}
|
||||
|
||||
[authResult.user linkWithCredential:unverifiedProviderCredential completion:completeSignInBlock];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleAnonymousUpgrade:(NSString *)anonymousUserID email:(NSString *)email {
|
||||
// Check for the presence of an anonymous user and whether automatic upgrade is enabled.
|
||||
if (self.authUI.auth.currentUser.isAnonymous &&
|
||||
self.authUI.shouldAutoUpgradeAnonymousUsers &&
|
||||
[anonymousUserID isEqualToString:self.authUI.auth.currentUser.uid]) {
|
||||
|
||||
FIRAuthCredential *credential =
|
||||
[FIREmailAuthProvider credentialWithEmail:email link:self.emailLink];
|
||||
|
||||
void (^completeSignInBlock)(FIRAuthDataResult *, NSError *) = ^(FIRAuthDataResult *authResult,
|
||||
NSError *error) {
|
||||
if (error) {
|
||||
switch (error.code) {
|
||||
case FIRAuthErrorCodeWrongPassword:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_WrongPasswordError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserNotFound:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_UserNotFoundError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserDisabled:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_AccountDisabledError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeTooManyRequests:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_SignInTooManyTimesError)];
|
||||
return;
|
||||
}
|
||||
}
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_SignedIn)];
|
||||
};
|
||||
|
||||
[self.authUI.auth.currentUser
|
||||
linkWithCredential:credential
|
||||
completion:^(FIRAuthDataResult *_Nullable authResult,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
if (error.code == FIRAuthErrorCodeEmailAlreadyInUse) {
|
||||
NSDictionary *userInfo = @{ FUIAuthCredentialKey : credential };
|
||||
NSError *mergeError = [FUIAuthErrorUtils mergeConflictErrorWithUserInfo:userInfo
|
||||
underlyingError:error];
|
||||
completeSignInBlock(nil, mergeError);
|
||||
return;
|
||||
}
|
||||
completeSignInBlock(nil, error);
|
||||
return;
|
||||
}
|
||||
completeSignInBlock(authResult, nil);
|
||||
}];
|
||||
} else {
|
||||
[self handleDifferentDevice];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleEmaiLinkSignIn:(NSString *)email {
|
||||
FIRAuthCredential *credential =
|
||||
[FIREmailAuthProvider credentialWithEmail:email link:self.emailLink];
|
||||
|
||||
void (^completeSignInBlock)(FIRAuthDataResult *, NSError *) = ^(FIRAuthDataResult *authResult,
|
||||
NSError *error) {
|
||||
if (error) {
|
||||
switch (error.code) {
|
||||
case FIRAuthErrorCodeWrongPassword:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_WrongPasswordError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserNotFound:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_UserNotFoundError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserDisabled:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_AccountDisabledError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeTooManyRequests:
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_SignInTooManyTimesError)];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void (^dismissHandler)(void) = ^() {
|
||||
UINavigationController *authViewController = [self.authUI authViewController];
|
||||
if (!(authViewController.isViewLoaded && authViewController.view.window)) {
|
||||
[authViewController.navigationController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:authResult URL:nil error:error];
|
||||
};
|
||||
|
||||
[FUIAuthBaseViewController showAlertWithTitle:FUILocalizedString(kStr_SignedIn)
|
||||
message:nil
|
||||
actionTitle:nil
|
||||
actionHandler:nil
|
||||
dismissTitle:FUILocalizedString(kStr_OK)
|
||||
dismissHandler:dismissHandler
|
||||
presentingViewController:nil];
|
||||
};
|
||||
|
||||
[self.authUI.auth signInWithCredential:credential completion:completeSignInBlock];
|
||||
}
|
||||
|
||||
- (void)handleDifferentDevice {
|
||||
UINavigationController *authViewController = [self.authUI authViewController];
|
||||
void (^completion)(void) = ^(){
|
||||
[FUIAuthBaseViewController showAlertWithTitle:@"New Device detected"
|
||||
message:@"Try opening the link using the same "
|
||||
"device where you started the sign-in process"
|
||||
presentingViewController:authViewController];
|
||||
};
|
||||
|
||||
if (!(authViewController.isViewLoaded && authViewController.view.window)) {
|
||||
[UIApplication.sharedApplication.keyWindow.rootViewController
|
||||
presentViewController:authViewController animated:YES completion:completion];
|
||||
} else {
|
||||
completion();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleConfirmEmail {
|
||||
UINavigationController *authViewController = [self.authUI authViewController];
|
||||
void (^completion)(void) = ^(){
|
||||
UIViewController *controller = [[FUIConfirmEmailViewController alloc] initWithAuthUI:self.authUI];
|
||||
[authViewController pushViewController:controller animated:YES];
|
||||
};
|
||||
|
||||
if (!(authViewController.isViewLoaded && authViewController.view.window)) {
|
||||
[UIApplication.sharedApplication.keyWindow.rootViewController
|
||||
presentViewController:authViewController animated:YES completion:completion];
|
||||
} else {
|
||||
completion();
|
||||
}
|
||||
}
|
||||
|
||||
/** @fn callbackWithCredential:error:
|
||||
@brief Ends the sign-in flow by cleaning up and calling back with given credential or error.
|
||||
@param credential The credential to pass back, if any.
|
||||
@param error The error to pass back, if any.
|
||||
@param result The result of sign-in operation using provided @c FIRAuthCredential object.
|
||||
@see @c FIRAuth.signInWithCredential:completion:
|
||||
*/
|
||||
- (void)callbackWithCredential:(nullable FIRAuthCredential *)credential
|
||||
error:(nullable NSError *)error
|
||||
result:(nullable FIRAuthResultCallback)result {
|
||||
FUIAuthProviderSignInCompletionBlock callback = self.pendingSignInCallback;
|
||||
self.pendingSignInCallback = nil;
|
||||
if (callback) {
|
||||
callback(credential, error, result, nil);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - FUIEmailAuthProvider
|
||||
|
||||
- (void)signInWithEmailHint:(NSString *)emailHint
|
||||
presentingViewController:(FUIAuthBaseViewController *)presentingViewController
|
||||
originalError:(NSError *)originalError
|
||||
completion:(FUIEmailHintSignInCallback)completion {
|
||||
NSString *kTempApp = @"tempApp";
|
||||
FIROptions *options = [FIROptions defaultOptions];
|
||||
// Create an new app instance in order to create a new auth instance.
|
||||
if (![FIRApp appNamed:kTempApp]) {
|
||||
[FIRApp configureWithName:kTempApp options:options];
|
||||
}
|
||||
FIRApp *tempApp = [FIRApp appNamed:kTempApp];
|
||||
// Create a new auth instance in order to perform a successful sign-in without losing the
|
||||
// currently signed in user on the default auth instance.
|
||||
FIRAuth *tempAuth = [FIRAuth authWithApp:tempApp];
|
||||
|
||||
[self.authUI.auth fetchSignInMethodsForEmail:emailHint
|
||||
completion:^(NSArray<NSString *> *_Nullable providers,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
if (completion) {
|
||||
completion(nil, error, nil);
|
||||
}
|
||||
return;
|
||||
}
|
||||
NSString *existingFederatedProviderID = [self authProviderFromProviders:providers];
|
||||
// Set of providers which can be auto-linked.
|
||||
NSSet *supportedProviders =
|
||||
[NSSet setWithObjects:FIRGoogleAuthProviderID,
|
||||
FIRFacebookAuthProviderID,
|
||||
FIREmailAuthProviderID,
|
||||
nil];
|
||||
if ([supportedProviders containsObject:existingFederatedProviderID]) {
|
||||
if ([existingFederatedProviderID isEqualToString:FIREmailAuthProviderID]) {
|
||||
|
||||
[FUIAuthBaseViewController showSignInAlertWithEmail:emailHint
|
||||
providerShortName:@"Email/Password"
|
||||
providerSignInLabel:@"Sign in with Email/Password"
|
||||
presentingViewController:presentingViewController
|
||||
signinHandler:^{
|
||||
FUIAuth *authUI = [FUIAuth authUIWithAuth:tempAuth];
|
||||
// Email password sign-in
|
||||
FUIPasswordSignInViewController *controller =
|
||||
[[FUIPasswordSignInViewController alloc] initWithAuthUI:authUI email:emailHint];
|
||||
controller.onDismissCallback = ^(FIRAuthDataResult *result, NSError *error) {
|
||||
if (completion) {
|
||||
completion(result, error, nil);
|
||||
}
|
||||
};
|
||||
[presentingViewController pushViewController:controller];
|
||||
}
|
||||
cancelHandler:^{
|
||||
if (completion) {
|
||||
completion(nil, originalError, nil);
|
||||
}
|
||||
}];
|
||||
} else { // Federated sign-in case.
|
||||
id<FUIAuthProvider> authProviderUI;
|
||||
// Retrieve the FUIAuthProvider instance from FUIAuth for the existing provider ID.
|
||||
for (id<FUIAuthProvider> provider in self.authUI.providers) {
|
||||
if ([provider.providerID isEqualToString:existingFederatedProviderID]) {
|
||||
authProviderUI = provider;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[FUIAuthBaseViewController showSignInAlertWithEmail:emailHint
|
||||
provider:authProviderUI
|
||||
presentingViewController:presentingViewController
|
||||
signinHandler:^{
|
||||
[authProviderUI signOut];
|
||||
[authProviderUI signInWithDefaultValue:emailHint
|
||||
presentingViewController:presentingViewController
|
||||
completion:^(FIRAuthCredential *_Nullable credential,
|
||||
NSError *_Nullable error,
|
||||
FIRAuthResultCallback _Nullable result,
|
||||
NSDictionary *_Nullable userInfo) {
|
||||
if (error) {
|
||||
if (completion) {
|
||||
completion(nil, error, nil);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
[tempAuth signInWithCredential:credential
|
||||
completion:^(FIRAuthDataResult *_Nullable authResult,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
if (completion) {
|
||||
completion(nil, error, nil);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle potential email mismatch.
|
||||
if (![emailHint isEqualToString:authResult.user.email]) {
|
||||
NSString *signedInEmail = authResult.user.email;
|
||||
NSString *title =
|
||||
[NSString stringWithFormat:@"Continue sign in with %@?", signedInEmail];
|
||||
NSString *message =
|
||||
[NSString stringWithFormat:@"You originally wanted to sign in with %@",
|
||||
emailHint];
|
||||
[FUIAuthBaseViewController showAlertWithTitle:title
|
||||
message:message
|
||||
actionTitle:@"Continue"
|
||||
actionHandler:^{
|
||||
if (completion) {
|
||||
completion(authResult, nil, credential);
|
||||
}
|
||||
}
|
||||
dismissTitle:@"Cancel"
|
||||
dismissHandler:^{
|
||||
if (completion) {
|
||||
completion(nil, error, credential);
|
||||
}
|
||||
}
|
||||
presentingViewController:presentingViewController];
|
||||
}
|
||||
if (completion) {
|
||||
completion(authResult, error, credential);
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}
|
||||
cancelHandler:^{
|
||||
if (completion) {
|
||||
completion(nil, originalError, nil);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)handleAccountLinkingForEmail:(NSString *)email
|
||||
newCredential:(FIRAuthCredential *)newCredential
|
||||
presentingViewController:(UIViewController *)presentingViewController
|
||||
signInResult:(_Nullable FIRAuthResultCallback)result {
|
||||
id<FUIAuthDelegate> delegate = self.authUI.delegate;
|
||||
[self.authUI.auth fetchSignInMethodsForEmail:email
|
||||
completion:^(NSArray<NSString *> *_Nullable providers,
|
||||
NSError *_Nullable error) {
|
||||
if (result) {
|
||||
result(nil, error);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (error.code == FIRAuthErrorCodeInvalidEmail) {
|
||||
// This should never happen because the email address comes from the backend.
|
||||
[FUIAuthBaseViewController showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)
|
||||
presentingViewController:presentingViewController];
|
||||
} else {
|
||||
[presentingViewController dismissViewControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:nil URL:nil error:error];
|
||||
}];
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!providers.count) {
|
||||
// This should never happen because the user must be registered.
|
||||
[FUIAuthBaseViewController showAlertWithMessage:
|
||||
FUILocalizedString(kStr_CannotAuthenticateError)
|
||||
presentingViewController:presentingViewController];
|
||||
return;
|
||||
}
|
||||
NSString *bestProviderID = providers[0];
|
||||
if ([bestProviderID isEqual:FIREmailAuthProviderID]) {
|
||||
// Password verification.
|
||||
UIViewController *passwordController;
|
||||
if ([delegate respondsToSelector:
|
||||
@selector(passwordVerificationViewControllerForAuthUI:email:newCredential:)]) {
|
||||
|
||||
passwordController = [delegate passwordVerificationViewControllerForAuthUI:self.authUI
|
||||
email:email
|
||||
newCredential:newCredential];
|
||||
} else {
|
||||
passwordController =
|
||||
[[FUIPasswordVerificationViewController alloc] initWithAuthUI:self.authUI
|
||||
email:email
|
||||
newCredential:newCredential];
|
||||
}
|
||||
if (presentingViewController.navigationController) {
|
||||
[FUIAuthBaseViewController pushViewController:passwordController
|
||||
navigationController:
|
||||
presentingViewController.navigationController];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ([bestProviderID isEqual:FIREmailLinkAuthSignInMethod]) {
|
||||
NSString *providerName;
|
||||
if ([newCredential.provider isEqualToString:FIRFacebookAuthProviderID]) {
|
||||
providerName = @"Facebook";
|
||||
} else if ([newCredential.provider isEqualToString:FIRTwitterAuthProviderID]) {
|
||||
providerName = @"Twitter";
|
||||
} else if ([newCredential.provider isEqualToString:FIRGitHubAuthProviderID]) {
|
||||
providerName = @"Github";
|
||||
}
|
||||
NSString *message = [NSString stringWithFormat:
|
||||
@"You already have an account\n \n You've already used %@. You "
|
||||
"can connect your %@ account with %@ by signing in with Email "
|
||||
"link below. \n \n For this flow to successfully connect your "
|
||||
"account with this email, you have to open the link on the same "
|
||||
"device or browser.", email, providerName, email];
|
||||
void (^actionHandler)(void) = ^() {
|
||||
[self generateURLParametersAndLocalCache:email
|
||||
linkingProvider:newCredential.provider];
|
||||
|
||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:newCredential];
|
||||
[GULUserDefaults.standardUserDefaults setObject:data forKey:kEmailLinkSignInLinkingCredentialKey];
|
||||
|
||||
void (^completion)(NSError * _Nullable error) = ^(NSError * _Nullable error){
|
||||
if (error) {
|
||||
[FUIAuthBaseViewController showAlertWithMessage:error.description];
|
||||
} else {
|
||||
NSString *signInMessage = [NSString stringWithFormat:
|
||||
@"A sign-in email with additional instructions was sent to %@. Check your "
|
||||
"email to complete sign-in.", email];
|
||||
[FUIAuthBaseViewController
|
||||
showAlertWithTitle:@"Sign-in email sent"
|
||||
message:signInMessage
|
||||
presentingViewController:nil];
|
||||
}
|
||||
};
|
||||
[self.authUI.auth sendSignInLinkToEmail:email
|
||||
actionCodeSettings:self.actionCodeSettings
|
||||
completion:completion];
|
||||
};
|
||||
|
||||
[FUIAuthBaseViewController
|
||||
showAlertWithTitle:@"Sign in"
|
||||
message:message
|
||||
actionTitle:@"Sign in"
|
||||
actionHandler:actionHandler
|
||||
dismissTitle:nil
|
||||
dismissHandler:nil
|
||||
presentingViewController:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
id<FUIAuthProvider> bestProvider = [self.authUI providerWithID:bestProviderID];
|
||||
if (!bestProvider) {
|
||||
// Unsupported provider.
|
||||
[FUIAuthBaseViewController showAlertWithMessage:
|
||||
FUILocalizedString(kStr_CannotAuthenticateError)
|
||||
presentingViewController:presentingViewController];
|
||||
return;
|
||||
}
|
||||
|
||||
[FUIAuthBaseViewController showSignInAlertWithEmail:email
|
||||
provider:bestProvider
|
||||
presentingViewController:presentingViewController
|
||||
signinHandler:^{
|
||||
// Sign out first to make sure sign in starts with a clean state.
|
||||
[bestProvider signOut];
|
||||
[bestProvider signInWithDefaultValue:email
|
||||
presentingViewController:presentingViewController
|
||||
completion:^(FIRAuthCredential *_Nullable credential,
|
||||
NSError *_Nullable error,
|
||||
_Nullable FIRAuthResultCallback result,
|
||||
NSDictionary *_Nullable userInfo) {
|
||||
if (error) {
|
||||
if (error.code == FUIAuthErrorCodeUserCancelledSignIn) {
|
||||
// User cancelled sign in, Do nothing.
|
||||
if (result) {
|
||||
result(nil, error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:nil URL:nil error:error];
|
||||
return;
|
||||
}
|
||||
|
||||
[self.authUI.auth signInWithCredential:credential
|
||||
completion:^(FIRAuthDataResult*_Nullable authResult,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:nil URL:nil error:error];
|
||||
if (result) {
|
||||
result(nil, error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
FIRUser *user = authResult.user;
|
||||
[user linkWithCredential:newCredential
|
||||
completion:^(FIRAuthDataResult *_Nullable authResult,
|
||||
NSError *_Nullable error) {
|
||||
if (result) {
|
||||
result(authResult.user, error);
|
||||
}
|
||||
// Ignore any error (most likely caused by email mismatch) and treat the user as
|
||||
// successfully signed in.
|
||||
[presentingViewController dismissViewControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:authResult URL:nil error:nil];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
} cancelHandler:^{
|
||||
[self.authUI signOutWithError:nil];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)generateURLParametersAndLocalCache:(NSString *)email linkingProvider:(NSString *)linkingProvider {
|
||||
NSURL *url = self.actionCodeSettings.URL;
|
||||
NSURLComponents *urlComponents = [NSURLComponents componentsWithString:url.absoluteString];
|
||||
NSMutableArray<NSURLQueryItem *> *urlQuertItems = [NSMutableArray array];
|
||||
|
||||
[GULUserDefaults.standardUserDefaults setObject:email forKey:kEmailLinkSignInEmailKey];
|
||||
|
||||
if (self.authUI.auth.currentUser.isAnonymous && self.authUI.shouldAutoUpgradeAnonymousUsers) {
|
||||
NSString *auid = self.authUI.auth.currentUser.uid;
|
||||
|
||||
NSURLQueryItem *anonymousUserIDQueryItem =
|
||||
[NSURLQueryItem queryItemWithName:@"ui_auid" value:auid];
|
||||
[urlQuertItems addObject:anonymousUserIDQueryItem];
|
||||
}
|
||||
|
||||
NSInteger ui_sid = arc4random_uniform(999999999);
|
||||
NSString *sidString = [NSString stringWithFormat:@"%ld", (long)ui_sid];
|
||||
[GULUserDefaults.standardUserDefaults setObject:sidString forKey:@"ui_sid"];
|
||||
|
||||
NSURLQueryItem *sessionIDQueryItem =
|
||||
[NSURLQueryItem queryItemWithName:@"ui_sid" value:sidString];
|
||||
[urlQuertItems addObject:sessionIDQueryItem];
|
||||
|
||||
NSString *sameDeviceValueString;
|
||||
if (self.forceSameDevice) {
|
||||
sameDeviceValueString = @"1";
|
||||
} else {
|
||||
sameDeviceValueString = @"0";
|
||||
}
|
||||
NSURLQueryItem *sameDeviceQueryItem = [NSURLQueryItem queryItemWithName:@"ui_sd" value:sameDeviceValueString];
|
||||
[urlQuertItems addObject:sameDeviceQueryItem];
|
||||
|
||||
if (linkingProvider) {
|
||||
NSURLQueryItem *providerIDQueryItem = [NSURLQueryItem queryItemWithName:@"ui_pid" value:linkingProvider];
|
||||
[urlQuertItems addObject:providerIDQueryItem];
|
||||
}
|
||||
|
||||
urlComponents.queryItems = urlQuertItems;
|
||||
self.actionCodeSettings.URL = urlComponents.URL;
|
||||
}
|
||||
|
||||
- (nullable NSString *)authProviderFromProviders:(NSArray <NSString *> *) providers {
|
||||
NSSet *providerSet =
|
||||
[NSSet setWithArray:@[ FIRFacebookAuthProviderID,
|
||||
FIRGoogleAuthProviderID,
|
||||
FIREmailAuthProviderID ]];
|
||||
for (NSString *provider in providers) {
|
||||
if ( [providerSet containsObject:provider]) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// 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 <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/* Name of the FirebaseEmailAuthUI resource bundle. */
|
||||
extern NSString *const FUIEmailAuthBundleName;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @fn FUIEmailAuthLocalizedString
|
||||
@brief Gets a localized string from a name.
|
||||
@param key The key value of the string.
|
||||
@return The string by the key localized in the current locale.
|
||||
*/
|
||||
NSString *FUIEmailAuthLocalizedString(NSString *key);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// 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 "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailAuth.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuthStrings.h"
|
||||
|
||||
#if SWIFT_PACKAGE
|
||||
NSString *const FUIEmailAuthBundleName = @"FirebaseUI_FirebaseEmailAuthUI";
|
||||
#else
|
||||
NSString *const FUIEmailAuthBundleName = @"FirebaseEmailAuthUI";
|
||||
#endif // SWIFT_PACKAGE
|
||||
|
||||
/** @var kEmailAuthProviderTableName
|
||||
@brief The name of the strings table to search for localized strings.
|
||||
*/
|
||||
NSString *const kEmailAuthProviderTableName = @"FirebaseEmailAuthUI";
|
||||
|
||||
NSString *FUIEmailAuthLocalizedString(NSString *key) {
|
||||
return FUILocalizedStringFromTableInBundle(key,
|
||||
kEmailAuthProviderTableName,
|
||||
[FUIEmailAuth bundle]);
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailAuth.h"
|
||||
|
||||
#import <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FUIEmailAuth (Internal)
|
||||
|
||||
/** @fn callbackWithCredential:error:
|
||||
@brief Ends the sign-in flow by cleaning up and calling back with given credential or error.
|
||||
@param credential The credential to pass back, if any.
|
||||
@param error The error to pass back, if any.
|
||||
@param result The result of sign-in operation using provided @c FIRAuthCredential object.
|
||||
@see @c FIRAuth.signInWithCredential:completion:
|
||||
*/
|
||||
- (void)callbackWithCredential:(nullable FIRAuthCredential *)credential
|
||||
error:(nullable NSError *)error
|
||||
result:(nullable FIRAuthResultCallback)result;
|
||||
|
||||
/** @fn alertControllerForError:actionHandler:
|
||||
@brief Creates alert controller for specified email auth error.
|
||||
@param error The error which should be shown in alert.
|
||||
@param actionHandler The handler of alert action button, if any.
|
||||
*/
|
||||
+ (UIAlertController *)alertControllerForError:(NSError *)error
|
||||
actionHandler:(nullable FUIAuthAlertActionHandler)actionHandler;
|
||||
|
||||
/** @fn generateURLParametersAndLocalCache:linkingProvider:
|
||||
@brief Generate the parameters before sending out the email link. Append the parameters to
|
||||
continue url and store them locally.
|
||||
@param email The email that requested the email sign in link.
|
||||
@param linkingProvider The id of the auth provider to be linked, if any.
|
||||
*/
|
||||
- (void)generateURLParametersAndLocalCache:(NSString *)email linkingProvider:(nullable NSString *)linkingProvider;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
Generated
Executable
+378
@@ -0,0 +1,378 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailEntryViewController.h"
|
||||
|
||||
#import <FirebaseAuth/FirebaseAuth.h>
|
||||
|
||||
#import <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailAuth.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuth_Internal.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuthStrings.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordSignInViewController.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordSignUpViewController.h"
|
||||
|
||||
/** @var kCellReuseIdentifier
|
||||
@brief The reuse identifier for table view cell.
|
||||
*/
|
||||
static NSString *const kCellReuseIdentifier = @"cellReuseIdentifier";
|
||||
|
||||
/** @var kAppIDCodingKey
|
||||
@brief The key used to encode the app ID for NSCoding.
|
||||
*/
|
||||
static NSString *const kAppIDCodingKey = @"appID";
|
||||
|
||||
/** @var kAuthUICodingKey
|
||||
@brief The key used to encode @c FUIAuth instance for NSCoding.
|
||||
*/
|
||||
static NSString *const kAuthUICodingKey = @"authUI";
|
||||
|
||||
/** @var kEmailCellAccessibilityID
|
||||
@brief The Accessibility Identifier for the @c email sign in cell.
|
||||
*/
|
||||
static NSString *const kEmailCellAccessibilityID = @"EmailCellAccessibilityID";
|
||||
|
||||
/** @var kNextButtonAccessibilityID
|
||||
@brief The Accessibility Identifier for the @c next button.
|
||||
*/
|
||||
static NSString *const kNextButtonAccessibilityID = @"NextButtonAccessibilityID";
|
||||
|
||||
@interface FUIEmailEntryViewController () <UITableViewDataSource, UITextFieldDelegate>
|
||||
@end
|
||||
|
||||
@implementation FUIEmailEntryViewController {
|
||||
/** @var _emailField
|
||||
@brief The @c UITextField that user enters email address into.
|
||||
*/
|
||||
UITextField *_emailField;
|
||||
|
||||
/** @var _tableView
|
||||
@brief The @c UITableView used to store all UI elements.
|
||||
*/
|
||||
__weak IBOutlet UITableView *_tableView;
|
||||
|
||||
/** @var _termsOfServiceView
|
||||
@brief The @c Text view which displays Terms of Service.
|
||||
*/
|
||||
__weak IBOutlet FUIPrivacyAndTermsOfServiceView *_termsOfServiceView;
|
||||
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI {
|
||||
return [self initWithNibName:NSStringFromClass([self class])
|
||||
bundle:[FUIEmailAuth bundle]
|
||||
authUI:authUI];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(NSString *)nibNameOrNil
|
||||
bundle:(NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI {
|
||||
|
||||
self = [super initWithNibName:nibNameOrNil
|
||||
bundle:nibBundleOrNil
|
||||
authUI:authUI];
|
||||
if (self) {
|
||||
self.title = FUILocalizedString(kStr_EnterYourEmail);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
UIBarButtonItem *nextButtonItem =
|
||||
[FUIAuthBaseViewController barItemWithTitle:FUILocalizedString(kStr_Next)
|
||||
target:self
|
||||
action:@selector(next)];
|
||||
nextButtonItem.accessibilityIdentifier = kNextButtonAccessibilityID;
|
||||
self.navigationItem.rightBarButtonItem = nextButtonItem;
|
||||
_termsOfServiceView.authUI = self.authUI;
|
||||
[_termsOfServiceView useFullMessage];
|
||||
|
||||
[self enableDynamicCellHeightForTableView:_tableView];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (self.navigationController.viewControllers.firstObject == self) {
|
||||
if (!self.authUI.shouldHideCancelButton) {
|
||||
UIBarButtonItem *cancelBarButton =
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
|
||||
target:self
|
||||
action:@selector(cancelAuthorization)];
|
||||
self.navigationItem.leftBarButtonItem = cancelBarButton;
|
||||
}
|
||||
self.navigationItem.backBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithTitle:FUILocalizedString(kStr_Back)
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:nil
|
||||
action:nil];
|
||||
|
||||
if (@available(iOS 13, *)) {
|
||||
if (!self.authUI.isInteractiveDismissEnabled) {
|
||||
self.modalInPresentation = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)next {
|
||||
[self onNext:_emailField.text];
|
||||
}
|
||||
|
||||
- (void)onNext:(NSString *)emailText {
|
||||
FUIEmailAuth *emailAuth = [self.authUI providerWithID:FIREmailAuthProviderID];
|
||||
id<FUIAuthDelegate> delegate = self.authUI.delegate;
|
||||
|
||||
if (![[self class] isValidEmail:emailText]) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)];
|
||||
return;
|
||||
}
|
||||
|
||||
[self incrementActivity];
|
||||
|
||||
[self.auth fetchSignInMethodsForEmail:emailText
|
||||
completion:^(NSArray<NSString *> *_Nullable providers,
|
||||
NSError *_Nullable error) {
|
||||
[self decrementActivity];
|
||||
|
||||
if (error) {
|
||||
if (error.code == FIRAuthErrorCodeInvalidEmail) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)];
|
||||
} else {
|
||||
[self dismissNavigationControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:nil URL:nil error:error];
|
||||
}];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
id<FUIAuthProvider> provider = [self bestProviderFromProviderIDs:providers];
|
||||
if (provider && ![provider.providerID isEqualToString:FIREmailAuthProviderID]) {
|
||||
NSString *email = emailText;
|
||||
[[self class] showSignInAlertWithEmail:email
|
||||
provider:provider
|
||||
presentingViewController:self
|
||||
signinHandler:^{
|
||||
[self signInWithProvider:provider email:email];
|
||||
}
|
||||
cancelHandler:^{
|
||||
[self.authUI signOutWithError:nil];
|
||||
}];
|
||||
} else if ([providers containsObject:FIREmailAuthProviderID]) {
|
||||
UIViewController *controller;
|
||||
if ([delegate respondsToSelector:@selector(passwordSignInViewControllerForAuthUI:email:)]) {
|
||||
controller = [delegate passwordSignInViewControllerForAuthUI:self.authUI
|
||||
email:emailText];
|
||||
} else {
|
||||
controller = [[FUIPasswordSignInViewController alloc] initWithAuthUI:self.authUI
|
||||
email:emailText];
|
||||
}
|
||||
[self pushViewController:controller];
|
||||
} else if ([emailAuth.signInMethod isEqualToString:FIREmailLinkAuthSignInMethod]) {
|
||||
[self sendSignInLinkToEmail:emailText];
|
||||
} else {
|
||||
if (providers.count) {
|
||||
// There's some unsupported providers, surface the error to the user.
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_CannotAuthenticateError)];
|
||||
} else {
|
||||
// New user.
|
||||
UIViewController *controller;
|
||||
if (emailAuth.allowNewEmailAccounts) {
|
||||
if ([delegate respondsToSelector:@selector(passwordSignUpViewControllerForAuthUI:email:requireDisplayName:)]) {
|
||||
controller = [delegate passwordSignUpViewControllerForAuthUI:self.authUI
|
||||
email:emailText
|
||||
requireDisplayName:emailAuth.requireDisplayName];
|
||||
} else {
|
||||
controller = [[FUIPasswordSignUpViewController alloc] initWithAuthUI:self.authUI
|
||||
email:emailText
|
||||
requireDisplayName:emailAuth.requireDisplayName];
|
||||
}
|
||||
} else {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_UserNotFoundError)];
|
||||
}
|
||||
if (controller != nil) {
|
||||
[self pushViewController:controller];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sendSignInLinkToEmail:(NSString*)email {
|
||||
if (![[self class] isValidEmail:email]) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)];
|
||||
return;
|
||||
}
|
||||
|
||||
[self incrementActivity];
|
||||
FUIEmailAuth *emailAuth = [self.authUI providerWithID:FIREmailAuthProviderID];
|
||||
[emailAuth generateURLParametersAndLocalCache:email linkingProvider:nil];
|
||||
[self.auth sendSignInLinkToEmail:email
|
||||
actionCodeSettings:emailAuth.actionCodeSettings
|
||||
completion:^(NSError * _Nullable error) {
|
||||
[self decrementActivity];
|
||||
|
||||
if (error) {
|
||||
[FUIAuthBaseViewController showAlertWithTitle:FUILocalizedString(kStr_Error)
|
||||
message:error.description
|
||||
presentingViewController:self];
|
||||
} else {
|
||||
NSString *successMessage =
|
||||
[NSString stringWithFormat: FUILocalizedString(kStr_EmailSentConfirmationMessage), email];
|
||||
[FUIAuthBaseViewController showAlertWithTitle:FUILocalizedString(kStr_SignInEmailSent)
|
||||
message:successMessage
|
||||
actionTitle:FUILocalizedString(kStr_TroubleGettingEmailTitle)
|
||||
actionHandler:^{
|
||||
[FUIAuthBaseViewController
|
||||
showAlertWithTitle:FUILocalizedString(kStr_TroubleGettingEmailTitle)
|
||||
message:FUILocalizedString(kStr_TroubleGettingEmailMessage)
|
||||
actionTitle:FUILocalizedString(kStr_Resend)
|
||||
actionHandler:^{
|
||||
[self sendSignInLinkToEmail:email];
|
||||
} dismissTitle:FUILocalizedString(kStr_Back)
|
||||
dismissHandler:^{
|
||||
[self.navigationController popToRootViewControllerAnimated:YES];
|
||||
}
|
||||
presentingViewController:self];
|
||||
}
|
||||
dismissTitle:FUILocalizedString(kStr_Back)
|
||||
dismissHandler:^{
|
||||
[self.navigationController dismissViewControllerAnimated:YES
|
||||
completion:nil];
|
||||
}
|
||||
presentingViewController:self];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)textFieldDidChange {
|
||||
[self didChangeEmail:_emailField.text];
|
||||
}
|
||||
|
||||
- (void)didChangeEmail:(NSString *)emailText {
|
||||
self.navigationItem.rightBarButtonItem.enabled = (emailText.length > 0);
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView
|
||||
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FUIAuthTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
if (!cell) {
|
||||
UINib *cellNib = [UINib nibWithNibName:NSStringFromClass([FUIAuthTableViewCell class])
|
||||
bundle:[FUIAuthUtils authUIBundle]];
|
||||
[tableView registerNib:cellNib forCellReuseIdentifier:kCellReuseIdentifier];
|
||||
cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
}
|
||||
cell.label.text = FUILocalizedString(kStr_Email);
|
||||
cell.textField.placeholder = FUILocalizedString(kStr_EnterYourEmail);
|
||||
cell.textField.delegate = self;
|
||||
cell.accessibilityIdentifier = kEmailCellAccessibilityID;
|
||||
_emailField = cell.textField;
|
||||
cell.textField.secureTextEntry = NO;
|
||||
cell.textField.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
cell.textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
cell.textField.returnKeyType = UIReturnKeyNext;
|
||||
cell.textField.keyboardType = UIKeyboardTypeEmailAddress;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
cell.textField.textContentType = UITextContentTypeUsername;
|
||||
}
|
||||
[cell.textField addTarget:self
|
||||
action:@selector(textFieldDidChange)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
[self didChangeEmail:_emailField.text];
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (nullable id<FUIAuthProvider>)bestProviderFromProviderIDs:(NSArray<NSString *> *)providerIDs {
|
||||
NSArray<id<FUIAuthProvider>> *providers = self.authUI.providers;
|
||||
for (NSString *providerID in providerIDs) {
|
||||
for (id<FUIAuthProvider> provider in providers) {
|
||||
if ([providerID isEqual:provider.providerID]) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||
if (textField == _emailField) {
|
||||
[self onNext:_emailField.text];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Utilities
|
||||
|
||||
/** @fn signInWithProvider:email:
|
||||
@brief Actually kicks off sign in with the provider.
|
||||
@param provider The identity provider to sign in with.
|
||||
@param email The email address of the user.
|
||||
*/
|
||||
- (void)signInWithProvider:(id<FUIAuthProvider>)provider email:(NSString *)email {
|
||||
[self incrementActivity];
|
||||
|
||||
// Sign out first to make sure sign in starts with a clean state.
|
||||
[provider signOut];
|
||||
[provider signInWithDefaultValue:email
|
||||
presentingViewController:self
|
||||
completion:^(FIRAuthCredential *_Nullable credential,
|
||||
NSError *_Nullable error,
|
||||
_Nullable FIRAuthResultCallback result,
|
||||
NSDictionary *_Nullable userInfo) {
|
||||
if (error) {
|
||||
[self decrementActivity];
|
||||
if (result) {
|
||||
result(nil, error);
|
||||
}
|
||||
|
||||
[self dismissNavigationControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:nil URL:nil error:error];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
[self.auth signInWithCredential:credential
|
||||
completion:^(FIRAuthDataResult *_Nullable authResult,
|
||||
NSError *_Nullable error) {
|
||||
[self decrementActivity];
|
||||
if (result) {
|
||||
result(authResult.user, error);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:nil URL:nil error:error];
|
||||
} else {
|
||||
[self dismissNavigationControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:authResult URL:nil error:error];
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}
|
||||
@end
|
||||
Generated
Executable
+205
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordRecoveryViewController.h"
|
||||
|
||||
#import <FirebaseAuth/FirebaseAuth.h>
|
||||
#import <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailAuth.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuthStrings.h"
|
||||
|
||||
/** @var kCellReuseIdentifier
|
||||
@brief The reuse identifier for table view cell.
|
||||
*/
|
||||
static NSString *const kCellReuseIdentifier = @"cellReuseIdentifier";
|
||||
|
||||
/** @var kFooterTextViewHorizontalInset
|
||||
@brief The horizontal inset for @c footerTextView, which should match the iOS standard margin.
|
||||
*/
|
||||
static const CGFloat kFooterTextViewHorizontalInset = 8.0f;
|
||||
|
||||
@interface FUIPasswordRecoveryViewController () <UITableViewDataSource, UITextFieldDelegate>
|
||||
/** @property footerTextView
|
||||
@brief The text view in the footer of the table.
|
||||
*/
|
||||
@property(nonatomic, strong) IBOutlet UITextView *footerTextView;
|
||||
@property(nonatomic, strong) IBOutlet FUIPrivacyAndTermsOfServiceView *termsOfServiceView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FUIPasswordRecoveryViewController {
|
||||
/** @var _email
|
||||
@brief The @c email address of the user from the previous screen.
|
||||
*/
|
||||
NSString *_email;
|
||||
|
||||
/** @var _emailField
|
||||
@brief The @c UITextField that user enters email address into.
|
||||
*/
|
||||
UITextField *_emailField;
|
||||
|
||||
/** @var _tableView
|
||||
@brief The @c UITableView used to store all UI elements.
|
||||
*/
|
||||
__weak IBOutlet UITableView *_tableView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email {
|
||||
return [self initWithNibName:NSStringFromClass([self class])
|
||||
bundle:[FUIEmailAuth bundle]
|
||||
authUI:authUI
|
||||
email:email];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email {
|
||||
self = [super initWithNibName:nibNameOrNil
|
||||
bundle:nibBundleOrNil
|
||||
authUI:authUI];
|
||||
if (self) {
|
||||
_email = [email copy];
|
||||
|
||||
self.title = FUILocalizedString(kStr_PasswordRecoveryTitle);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
UIBarButtonItem *sendButtonItem =
|
||||
[FUIAuthBaseViewController barItemWithTitle:FUILocalizedString(kStr_Send)
|
||||
target:self
|
||||
action:@selector(send)];
|
||||
self.navigationItem.rightBarButtonItem = sendButtonItem;
|
||||
|
||||
[self enableDynamicCellHeightForTableView:_tableView];
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
_tableView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
self.footerTextView.textColor = [UIColor secondaryLabelColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
self.footerTextView.text = FUILocalizedString(kStr_PasswordRecoveryMessage);
|
||||
|
||||
// Adjust the footerTextView to have standard margins.
|
||||
self.footerTextView.textContainer.lineFragmentPadding = 0;
|
||||
_footerTextView.textContainerInset =
|
||||
UIEdgeInsetsMake(0, kFooterTextViewHorizontalInset, 0, kFooterTextViewHorizontalInset);
|
||||
[self.footerTextView sizeToFit];
|
||||
|
||||
self.termsOfServiceView.authUI = self.authUI;
|
||||
[self.termsOfServiceView useFooterMessage];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)send {
|
||||
[self recoverEmail:_emailField.text];
|
||||
}
|
||||
|
||||
- (void)recoverEmail:(NSString *)email {
|
||||
if (![[self class] isValidEmail:email]) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)];
|
||||
return;
|
||||
}
|
||||
|
||||
[self incrementActivity];
|
||||
|
||||
[self.auth sendPasswordResetWithEmail:email
|
||||
completion:^(NSError *_Nullable error) {
|
||||
[self decrementActivity];
|
||||
|
||||
if (error) {
|
||||
if (error.code == FIRAuthErrorCodeUserNotFound) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_UserNotFoundError)];
|
||||
return;
|
||||
}
|
||||
|
||||
[self dismissNavigationControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:nil URL:nil error:error];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *message = [NSString stringWithFormat:
|
||||
FUILocalizedString(kStr_PasswordRecoveryEmailSentMessage), email];
|
||||
[self showAlertWithMessage:message];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)textFieldDidChange {
|
||||
[self didChangeEmail:_emailField.text];
|
||||
}
|
||||
|
||||
- (void)didChangeEmail:(NSString *)email {
|
||||
self.navigationItem.rightBarButtonItem.enabled = (email.length > 0);
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView
|
||||
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FUIAuthTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
if (!cell) {
|
||||
UINib *cellNib = [UINib nibWithNibName:NSStringFromClass([FUIAuthTableViewCell class])
|
||||
bundle:[FUIAuthUtils authUIBundle]];
|
||||
[tableView registerNib:cellNib forCellReuseIdentifier:kCellReuseIdentifier];
|
||||
cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
}
|
||||
cell.label.text = FUILocalizedString(kStr_Email);
|
||||
_emailField = cell.textField;
|
||||
_emailField.delegate = self;
|
||||
_emailField.text = _email;
|
||||
_emailField.placeholder = FUILocalizedString(kStr_EnterYourEmail);
|
||||
_emailField.secureTextEntry = NO;
|
||||
_emailField.returnKeyType = UIReturnKeyNext;
|
||||
_emailField.keyboardType = UIKeyboardTypeEmailAddress;
|
||||
_emailField.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
_emailField.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_emailField.textContentType = UITextContentTypeUsername;
|
||||
}
|
||||
[cell.textField addTarget:self
|
||||
action:@selector(textFieldDidChange)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
[self didChangeEmail:_emailField.text];
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||
if (textField == _emailField) {
|
||||
[self send];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
Generated
Executable
+297
@@ -0,0 +1,297 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 "FUIPasswordSignInViewController_Internal.h"
|
||||
|
||||
#import <FirebaseAuth/FirebaseAuth.h>
|
||||
#import <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailAuth.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuthStrings.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordRecoveryViewController.h"
|
||||
|
||||
/** @var kCellReuseIdentifier
|
||||
@brief The reuse identifier for table view cell.
|
||||
*/
|
||||
static NSString *const kCellReuseIdentifier = @"cellReuseIdentifier";
|
||||
|
||||
@interface FUIPasswordSignInViewController () <UITableViewDataSource, UITextFieldDelegate>
|
||||
@end
|
||||
|
||||
@implementation FUIPasswordSignInViewController {
|
||||
/** @var _email
|
||||
@brief The @c email address of the user from the previous screen.
|
||||
*/
|
||||
NSString *_email;
|
||||
|
||||
/** @var _emailField
|
||||
@brief The @c UITextField that user enters email address into.
|
||||
*/
|
||||
UITextField *_emailField;
|
||||
|
||||
/** @var _passwordField
|
||||
@brief The @c UITextField that user enters password into.
|
||||
*/
|
||||
UITextField *_passwordField;
|
||||
|
||||
/** @var _tableView
|
||||
@brief The @c UITableView used to store all UI elements.
|
||||
*/
|
||||
__weak IBOutlet UITableView *_tableView;
|
||||
|
||||
/** @var _forgotPasswordButton
|
||||
@brief The @c UIButton which handles forgot password action.
|
||||
*/
|
||||
__weak IBOutlet UIButton *_forgotPasswordButton;
|
||||
|
||||
/** @var _termsOfServiceView
|
||||
@brief The @c Text view which displays Terms of Service.
|
||||
*/
|
||||
__weak IBOutlet FUIPrivacyAndTermsOfServiceView *_termsOfServiceView;
|
||||
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email {
|
||||
return [self initWithNibName:NSStringFromClass([self class])
|
||||
bundle:[FUIEmailAuth bundle]
|
||||
authUI:authUI
|
||||
email:email];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email {
|
||||
self = [super initWithNibName:nibNameOrNil
|
||||
bundle:nibBundleOrNil
|
||||
authUI:authUI];
|
||||
if (self) {
|
||||
_email = [email copy];
|
||||
|
||||
self.title = FUILocalizedString(kStr_SignInTitle);
|
||||
__weak FUIPasswordSignInViewController *weakself = self;
|
||||
_onDismissCallback = ^(FIRAuthDataResult *authResult, NSError *error){
|
||||
[weakself.authUI invokeResultCallbackWithAuthDataResult:authResult URL:nil error:error];
|
||||
};
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
UIBarButtonItem *signInButtonItem =
|
||||
[FUIAuthBaseViewController barItemWithTitle:FUILocalizedString(kStr_SignInTitle)
|
||||
target:self
|
||||
action:@selector(signIn)];
|
||||
self.navigationItem.rightBarButtonItem = signInButtonItem;
|
||||
[_forgotPasswordButton setTitle:FUILocalizedString(kStr_ForgotPasswordTitle)
|
||||
forState:UIControlStateNormal];
|
||||
_termsOfServiceView.authUI = self.authUI;
|
||||
[_termsOfServiceView useFooterMessage];
|
||||
|
||||
[self enableDynamicCellHeightForTableView:_tableView];
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
_tableView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (self.navigationController.viewControllers.firstObject == self) {
|
||||
if (!self.authUI.shouldHideCancelButton) {
|
||||
UIBarButtonItem *cancelBarButton =
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
|
||||
target:self
|
||||
action:@selector(cancelAuthorization)];
|
||||
self.navigationItem.leftBarButtonItem = cancelBarButton;
|
||||
}
|
||||
self.navigationItem.backBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithTitle:FUILocalizedString(kStr_Back)
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:nil
|
||||
action:nil];
|
||||
|
||||
if (@available(iOS 13, *)) {
|
||||
if (!self.authUI.isInteractiveDismissEnabled) {
|
||||
self.modalInPresentation = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)signInWithDefaultValue:(NSString *)email andPassword:(NSString *)password {
|
||||
if (![[self class] isValidEmail:email]) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)];
|
||||
return;
|
||||
}
|
||||
if (password.length <= 0) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidPasswordError)];
|
||||
return;
|
||||
}
|
||||
|
||||
[self incrementActivity];
|
||||
FIRAuthCredential *credential =
|
||||
[FIREmailAuthProvider credentialWithEmail:email password:password];
|
||||
|
||||
void (^completeSignInBlock)(FIRAuthDataResult *, NSError *) = ^(FIRAuthDataResult *authResult,
|
||||
NSError *error) {
|
||||
[self decrementActivity];
|
||||
|
||||
if (error) {
|
||||
switch (error.code) {
|
||||
case FIRAuthErrorCodeWrongPassword:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_WrongPasswordError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserNotFound:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_UserNotFoundError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeUserDisabled:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_AccountDisabledError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeTooManyRequests:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_SignInTooManyTimesError)];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self dismissNavigationControllerAnimated:YES completion:^{
|
||||
if (self->_onDismissCallback) {
|
||||
self->_onDismissCallback(authResult, error);
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
// Check for the presence of an anonymous user and whether automatic upgrade is enabled.
|
||||
if (self.auth.currentUser.isAnonymous && self.authUI.shouldAutoUpgradeAnonymousUsers) {
|
||||
[self.auth.currentUser
|
||||
linkWithCredential:credential
|
||||
completion:^(FIRAuthDataResult *_Nullable authResult,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
if (error.code == FIRAuthErrorCodeEmailAlreadyInUse) {
|
||||
NSDictionary *userInfo = @{ FUIAuthCredentialKey : credential };
|
||||
NSError *mergeError = [FUIAuthErrorUtils mergeConflictErrorWithUserInfo:userInfo
|
||||
underlyingError:error];
|
||||
completeSignInBlock(nil, mergeError);
|
||||
return;
|
||||
}
|
||||
completeSignInBlock(nil, error);
|
||||
return;
|
||||
}
|
||||
completeSignInBlock(authResult, nil);
|
||||
}];
|
||||
} else {
|
||||
[self.auth signInWithCredential:credential completion:completeSignInBlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)signIn {
|
||||
[self signInWithDefaultValue:_emailField.text andPassword:_passwordField.text];
|
||||
}
|
||||
|
||||
- (void)forgotPasswordForEmail:(NSString *)email {
|
||||
UIViewController *viewController;
|
||||
id<FUIAuthDelegate> delegate = self.authUI.delegate;
|
||||
if ([delegate respondsToSelector:@selector(passwordRecoveryViewControllerForAuthUI:email:)]) {
|
||||
viewController = [delegate passwordRecoveryViewControllerForAuthUI:self.authUI
|
||||
email:email];
|
||||
} else {
|
||||
viewController = [[FUIPasswordRecoveryViewController alloc] initWithAuthUI:self.authUI
|
||||
email:email];
|
||||
}
|
||||
[self pushViewController:viewController];
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)forgotPassword {
|
||||
[self forgotPasswordForEmail:_emailField.text];
|
||||
}
|
||||
|
||||
- (void)textFieldDidChange {
|
||||
[self didChangeEmail:_emailField.text andPassword:_passwordField.text];
|
||||
}
|
||||
|
||||
- (void)didChangeEmail:(NSString *)email andPassword:(NSString *)password {
|
||||
BOOL enableActionButton = email.length > 0 && password.length > 0;
|
||||
self.navigationItem.rightBarButtonItem.enabled = enableActionButton;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView
|
||||
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FUIAuthTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
if (!cell) {
|
||||
UINib *cellNib = [UINib nibWithNibName:NSStringFromClass([FUIAuthTableViewCell class])
|
||||
bundle:[FUIAuthUtils authUIBundle]];
|
||||
[tableView registerNib:cellNib forCellReuseIdentifier:kCellReuseIdentifier];
|
||||
cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
}
|
||||
cell.textField.delegate = self;
|
||||
if (indexPath.row == 0) {
|
||||
cell.label.text = FUILocalizedString(kStr_Email);
|
||||
cell.textField.enabled = _email == nil;
|
||||
_emailField = cell.textField;
|
||||
_emailField.text = _email;
|
||||
_emailField.placeholder = FUILocalizedString(kStr_EnterYourEmail);
|
||||
_emailField.secureTextEntry = NO;
|
||||
_emailField.returnKeyType = UIReturnKeyNext;
|
||||
_emailField.keyboardType = UIKeyboardTypeEmailAddress;
|
||||
_emailField.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
_emailField.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_emailField.textContentType = UITextContentTypeUsername;
|
||||
}
|
||||
} else if (indexPath.row == 1) {
|
||||
cell.label.text = FUILocalizedString(kStr_Password);
|
||||
_passwordField = cell.textField;
|
||||
_passwordField.placeholder = FUILocalizedString(kStr_EnterYourPassword);
|
||||
_passwordField.secureTextEntry = YES;
|
||||
_passwordField.returnKeyType = UIReturnKeyNext;
|
||||
_passwordField.keyboardType = UIKeyboardTypeDefault;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_passwordField.textContentType = UITextContentTypePassword;
|
||||
}
|
||||
}
|
||||
[cell.textField addTarget:self
|
||||
action:@selector(textFieldDidChange)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
[self didChangeEmail:_emailField.text andPassword:_passwordField.text];
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||
if (textField == _emailField) {
|
||||
[_passwordField becomeFirstResponder];
|
||||
} else if (textField == _passwordField) {
|
||||
[self signIn];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
Generated
+33
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// 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 "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordSignInViewController.h"
|
||||
#import <FirebaseAuth/FirebaseAuth.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FUIPasswordSignInViewController ()
|
||||
|
||||
/** @property onDismissCallback:
|
||||
@brief Sets an optional custom callback for FUIPasswordSigInViewController during dismissal. This block is NOT set to nil after use, set to nil after using
|
||||
if you wish to avoid circular references.
|
||||
*/
|
||||
@property(nonatomic, strong, nullable) FIRAuthDataResultCallback onDismissCallback;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
||||
@end
|
||||
Generated
Executable
+378
@@ -0,0 +1,378 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordSignUpViewController.h"
|
||||
|
||||
#import <FirebaseAuth/FirebaseAuth.h>
|
||||
#import <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailAuth.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuthStrings.h"
|
||||
|
||||
/** @var kCellReuseIdentifier
|
||||
@brief The reuse identifier for table view cell.
|
||||
*/
|
||||
static NSString *const kCellReuseIdentifier = @"cellReuseIdentifier";
|
||||
|
||||
/** @var kEmailSignUpCellAccessibilityID
|
||||
@brief The Accessibility Identifier for the @c email cell.
|
||||
*/
|
||||
static NSString *const kEmailSignUpCellAccessibilityID = @"EmailSignUpCellAccessibilityID";
|
||||
|
||||
/** @var kPasswordSignUpCellAccessibilityID
|
||||
@brief The Accessibility Identifier for the @c password cell.
|
||||
*/
|
||||
static NSString *const kPasswordSignUpCellAccessibilityID = @"PasswordSignUpCellAccessibilityID";
|
||||
|
||||
/** @var kNameSignUpCellAccessibilityID
|
||||
@brief The Accessibility Identifier for the @c name cell.
|
||||
*/
|
||||
static NSString *const kNameSignUpCellAccessibilityID = @"NameSignUpCellAccessibilityID";
|
||||
|
||||
/** @var kSaveButtonAccessibilityID
|
||||
@brief The Accessibility Identifier for the @c next button.
|
||||
*/
|
||||
static NSString *const kSaveButtonAccessibilityID = @"SaveButtonAccessibilityID";
|
||||
|
||||
/** @var kTextFieldRightViewSize
|
||||
@brief The height and width of the @c rightView of the password text field.
|
||||
*/
|
||||
static const CGFloat kTextFieldRightViewSize = 36.0f;
|
||||
|
||||
@interface FUIPasswordSignUpViewController () <UITableViewDataSource, UITextFieldDelegate>
|
||||
@end
|
||||
|
||||
@implementation FUIPasswordSignUpViewController {
|
||||
/** @var _email
|
||||
@brief The @c email address of the user from the previous screen.
|
||||
*/
|
||||
NSString *_email;
|
||||
|
||||
/** @var _emailField
|
||||
@brief The @c UITextField that user enters email address into.
|
||||
*/
|
||||
UITextField *_emailField;
|
||||
|
||||
/** @var _nameField
|
||||
@brief The @c UITextField that user enters name into.
|
||||
*/
|
||||
UITextField *_nameField;
|
||||
|
||||
/** @var requireDisplayName
|
||||
@brief Indicate weather display name field is required.
|
||||
*/
|
||||
BOOL _requireDisplayName;
|
||||
|
||||
/** @var _passwordField
|
||||
@brief The @c UITextField that user enters password into.
|
||||
*/
|
||||
UITextField *_passwordField;
|
||||
|
||||
/** @var _tableView
|
||||
@brief The @c UITableView used to store all UI elements.
|
||||
*/
|
||||
__weak IBOutlet UITableView *_tableView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email
|
||||
requireDisplayName:(BOOL)requireDisplayName {
|
||||
return [self initWithNibName:NSStringFromClass([self class])
|
||||
bundle:[FUIEmailAuth bundle]
|
||||
authUI:authUI
|
||||
email:email
|
||||
requireDisplayName:requireDisplayName];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email
|
||||
requireDisplayName:(BOOL)requireDisplayName {
|
||||
self = [super initWithNibName:nibNameOrNil
|
||||
bundle:nibBundleOrNil
|
||||
authUI:authUI];
|
||||
if (self) {
|
||||
_email = [email copy];
|
||||
_requireDisplayName = requireDisplayName;
|
||||
self.title = FUILocalizedString(kStr_SignUpTitle);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
UIBarButtonItem *saveButtonItem =
|
||||
[FUIAuthBaseViewController barItemWithTitle:FUILocalizedString(kStr_Save)
|
||||
target:self
|
||||
action:@selector(save)];
|
||||
saveButtonItem.accessibilityIdentifier = kSaveButtonAccessibilityID;
|
||||
self.navigationItem.rightBarButtonItem = saveButtonItem;
|
||||
|
||||
[self enableDynamicCellHeightForTableView:_tableView];
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
_tableView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
self.footerView.authUI = self.authUI;
|
||||
[self.footerView useFooterMessage];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)save {
|
||||
[self signUpWithEmail:_emailField.text
|
||||
andPassword:_passwordField.text
|
||||
andUsername:_nameField.text];
|
||||
}
|
||||
|
||||
- (void)signUpWithEmail:(NSString *)email
|
||||
andPassword:(NSString *)password
|
||||
andUsername:(NSString *)username {
|
||||
if (![[self class] isValidEmail:email]) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)];
|
||||
return;
|
||||
}
|
||||
if (password.length <= 0) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidPasswordError)];
|
||||
return;
|
||||
}
|
||||
|
||||
[self incrementActivity];
|
||||
|
||||
// Check for the presence of an anonymous user and whether automatic upgrade is enabled.
|
||||
if (self.auth.currentUser.isAnonymous && self.authUI.shouldAutoUpgradeAnonymousUsers) {
|
||||
FIRAuthCredential *credential =
|
||||
[FIREmailAuthProvider credentialWithEmail:email password:password];
|
||||
[self.auth.currentUser
|
||||
linkWithCredential:credential
|
||||
completion:^(FIRAuthDataResult *_Nullable authResult,
|
||||
NSError * _Nullable error) {
|
||||
if (error) {
|
||||
[self decrementActivity];
|
||||
[self finishSignUpWithAuthDataResult:nil error:error];
|
||||
return;
|
||||
}
|
||||
FIRUserProfileChangeRequest *request = [authResult.user profileChangeRequest];
|
||||
request.displayName = username;
|
||||
[request commitChangesWithCompletion:^(NSError *_Nullable error) {
|
||||
[self decrementActivity];
|
||||
|
||||
if (error) {
|
||||
[self finishSignUpWithAuthDataResult:nil error:error];
|
||||
return;
|
||||
}
|
||||
[self finishSignUpWithAuthDataResult:authResult error:nil];
|
||||
}];
|
||||
}];
|
||||
} else {
|
||||
[self.auth createUserWithEmail:email
|
||||
password:password
|
||||
completion:^(FIRAuthDataResult *_Nullable authDataResult,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
[self decrementActivity];
|
||||
|
||||
[self finishSignUpWithAuthDataResult:nil error:error];
|
||||
return;
|
||||
}
|
||||
|
||||
FIRUserProfileChangeRequest *request = [authDataResult.user profileChangeRequest];
|
||||
request.displayName = username;
|
||||
[request commitChangesWithCompletion:^(NSError *_Nullable error) {
|
||||
[self decrementActivity];
|
||||
|
||||
if (error) {
|
||||
[self finishSignUpWithAuthDataResult:nil error:error];
|
||||
return;
|
||||
}
|
||||
[self finishSignUpWithAuthDataResult:authDataResult error:nil];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)finishSignUpWithAuthDataResult:(nullable FIRAuthDataResult *)authDataResult
|
||||
error:(nullable NSError *)error {
|
||||
if (error) {
|
||||
switch (error.code) {
|
||||
case FIRAuthErrorCodeEmailAlreadyInUse:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_EmailAlreadyInUseError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeInvalidEmail:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeWeakPassword:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_WeakPasswordError)];
|
||||
return;
|
||||
case FIRAuthErrorCodeTooManyRequests:
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_SignUpTooManyTimesError)];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self dismissNavigationControllerAnimated:YES completion:^() {
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:authDataResult URL:nil error:error];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)textFieldDidChange {
|
||||
[self didChangeEmail:_emailField.text orPassword:_passwordField.text orUserName:_nameField.text];
|
||||
}
|
||||
|
||||
- (void)didChangeEmail:(NSString *)email
|
||||
orPassword:(NSString *)password
|
||||
orUserName:(NSString *)username {
|
||||
BOOL enableActionButton = email.length > 0 && password.length > 0;
|
||||
if (_requireDisplayName) {
|
||||
enableActionButton = enableActionButton && username.length > 0;
|
||||
}
|
||||
self.navigationItem.rightBarButtonItem.enabled = enableActionButton;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
if (_requireDisplayName) {
|
||||
return 3;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView
|
||||
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FUIAuthTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
if (!cell) {
|
||||
UINib *cellNib = [UINib nibWithNibName:NSStringFromClass([FUIAuthTableViewCell class])
|
||||
bundle:[FUIAuthUtils authUIBundle]];
|
||||
[tableView registerNib:cellNib forCellReuseIdentifier:kCellReuseIdentifier];
|
||||
cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
}
|
||||
cell.textField.delegate = self;
|
||||
if (indexPath.row == 0) {
|
||||
cell.label.text = FUILocalizedString(kStr_Email);
|
||||
cell.accessibilityIdentifier = kEmailSignUpCellAccessibilityID;
|
||||
cell.textField.enabled = NO;
|
||||
_emailField = cell.textField;
|
||||
_emailField.text = _email;
|
||||
_emailField.placeholder = FUILocalizedString(kStr_EnterYourEmail);
|
||||
_emailField.secureTextEntry = NO;
|
||||
_emailField.returnKeyType = UIReturnKeyNext;
|
||||
_emailField.keyboardType = UIKeyboardTypeEmailAddress;
|
||||
_emailField.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
_emailField.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_emailField.textContentType = UITextContentTypeUsername;
|
||||
}
|
||||
} else if (indexPath.row == 1) {
|
||||
if (_requireDisplayName) {
|
||||
cell.label.text = FUILocalizedString(kStr_Name);
|
||||
cell.accessibilityIdentifier = kNameSignUpCellAccessibilityID;
|
||||
_nameField = cell.textField;
|
||||
_nameField.placeholder = FUILocalizedString(kStr_FirstAndLastName);
|
||||
_nameField.secureTextEntry = NO;
|
||||
_nameField.returnKeyType = UIReturnKeyNext;
|
||||
_nameField.keyboardType = UIKeyboardTypeDefault;
|
||||
_nameField.autocapitalizationType = UITextAutocapitalizationTypeWords;
|
||||
if (@available(iOS 10.0, *)) {
|
||||
_nameField.textContentType = UITextContentTypeName;
|
||||
}
|
||||
} else {
|
||||
cell.label.text = FUILocalizedString(kStr_Password);
|
||||
cell.accessibilityIdentifier = kPasswordSignUpCellAccessibilityID;
|
||||
_passwordField = cell.textField;
|
||||
_passwordField.placeholder = FUILocalizedString(kStr_ChoosePassword);
|
||||
_passwordField.secureTextEntry = YES;
|
||||
_passwordField.rightView = [self visibilityToggleButtonForPasswordField];
|
||||
_passwordField.rightViewMode = UITextFieldViewModeAlways;
|
||||
_passwordField.returnKeyType = UIReturnKeyNext;
|
||||
_passwordField.keyboardType = UIKeyboardTypeDefault;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_passwordField.textContentType = UITextContentTypePassword;
|
||||
}
|
||||
}
|
||||
} else if (indexPath.row == 2) {
|
||||
cell.label.text = FUILocalizedString(kStr_Password);
|
||||
cell.accessibilityIdentifier = kPasswordSignUpCellAccessibilityID;
|
||||
_passwordField = cell.textField;
|
||||
_passwordField.placeholder = FUILocalizedString(kStr_ChoosePassword);
|
||||
_passwordField.secureTextEntry = YES;
|
||||
_passwordField.rightView = [self visibilityToggleButtonForPasswordField];
|
||||
_passwordField.rightViewMode = UITextFieldViewModeAlways;
|
||||
_passwordField.returnKeyType = UIReturnKeyNext;
|
||||
_passwordField.keyboardType = UIKeyboardTypeDefault;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_passwordField.textContentType = UITextContentTypePassword;
|
||||
}
|
||||
}
|
||||
[cell.textField addTarget:self
|
||||
action:@selector(textFieldDidChange)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
[self didChangeEmail:_emailField.text orPassword:_passwordField.text orUserName:_nameField.text];
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||
if (textField == _emailField) {
|
||||
[_nameField becomeFirstResponder];
|
||||
} else if (textField == _nameField) {
|
||||
[_passwordField becomeFirstResponder];
|
||||
} else if (textField == _passwordField) {
|
||||
[self signUpWithEmail:_emailField.text
|
||||
andPassword:_passwordField.text
|
||||
andUsername:_nameField.text];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Password field visibility toggle button
|
||||
|
||||
- (UIButton *)visibilityToggleButtonForPasswordField {
|
||||
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
button.frame = CGRectMake(0, 0, kTextFieldRightViewSize, kTextFieldRightViewSize);
|
||||
button.tintColor = [UIColor lightGrayColor];
|
||||
[self updateIconForRightViewButton:button];
|
||||
[button addTarget:self
|
||||
action:@selector(togglePasswordFieldVisibility:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
return button;
|
||||
}
|
||||
|
||||
- (void)updateIconForRightViewButton:(UIButton *)button {
|
||||
NSString *imageName = _passwordField.secureTextEntry ? @"ic_visibility" : @"ic_visibility_off";
|
||||
UIImage *image = [FUIAuthUtils imageNamed:imageName fromBundle:[FUIAuthUtils authUIBundle]];
|
||||
[button setImage:image forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
- (void)togglePasswordFieldVisibility:(UIButton *)button {
|
||||
// Make sure cursor is placed correctly by disabling and enabling the text field.
|
||||
_passwordField.enabled = NO;
|
||||
_passwordField.secureTextEntry = !_passwordField.secureTextEntry;
|
||||
[self updateIconForRightViewButton:button];
|
||||
_passwordField.enabled = YES;
|
||||
[_passwordField becomeFirstResponder];
|
||||
}
|
||||
|
||||
@end
|
||||
Generated
Executable
+238
@@ -0,0 +1,238 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordVerificationViewController.h"
|
||||
|
||||
#import <FirebaseAuth/FirebaseAuth.h>
|
||||
#import <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIEmailAuth.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/FUIEmailAuthStrings.h"
|
||||
#import "FirebaseEmailAuthUI/Sources/Public/FirebaseEmailAuthUI/FUIPasswordRecoveryViewController.h"
|
||||
|
||||
/** @var kCellReuseIdentifier
|
||||
@brief The reuse identifier for table view cell.
|
||||
*/
|
||||
static NSString *const kCellReuseIdentifier = @"cellReuseIdentifier";
|
||||
|
||||
@interface FUIPasswordVerificationViewController () <UITableViewDataSource, UITextFieldDelegate>
|
||||
@end
|
||||
|
||||
@implementation FUIPasswordVerificationViewController {
|
||||
/** @var _email
|
||||
@brief The @c The email address of the user collected previously.
|
||||
*/
|
||||
NSString *_email;
|
||||
|
||||
/** @var _newCredential
|
||||
@brief The new @c FIRAuthCredential that the user had never used before.
|
||||
*/
|
||||
FIRAuthCredential *_newCredential;
|
||||
|
||||
/** @var _passwordField
|
||||
@brief The @c UITextField that user enters password into.
|
||||
*/
|
||||
UITextField *_passwordField;
|
||||
|
||||
/** @var _tableView
|
||||
@brief The @c UITableView used to store all UI elements.
|
||||
*/
|
||||
__weak IBOutlet UITableView *_tableView;
|
||||
|
||||
/** @var _forgotPasswordButton
|
||||
@brief The @c UIButton which handles forgot password action.
|
||||
*/
|
||||
__weak IBOutlet UIButton *_forgotPasswordButton;
|
||||
|
||||
/** @var _termsOfServiceView
|
||||
@brief The @c Text view which displays Terms of Service.
|
||||
*/
|
||||
__weak IBOutlet FUIPrivacyAndTermsOfServiceView *_termsOfServiceView;
|
||||
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email
|
||||
newCredential:(FIRAuthCredential *)newCredential {
|
||||
return [self initWithNibName:NSStringFromClass([self class])
|
||||
bundle:[FUIEmailAuth bundle]
|
||||
authUI:authUI
|
||||
email:email
|
||||
newCredential:newCredential];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email
|
||||
newCredential:(FIRAuthCredential *)newCredential {
|
||||
self = [super initWithNibName:nibNameOrNil
|
||||
bundle:nibBundleOrNil
|
||||
authUI:authUI];
|
||||
if (self) {
|
||||
_email = [email copy];
|
||||
_newCredential = newCredential;
|
||||
self.title = FUILocalizedString(kStr_SignInTitle);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
UIBarButtonItem *nextButtonItem =
|
||||
[FUIAuthBaseViewController barItemWithTitle:FUILocalizedString(kStr_Next)
|
||||
target:self
|
||||
action:@selector(next)];
|
||||
self.navigationItem.rightBarButtonItem = nextButtonItem;
|
||||
|
||||
// The initial frame doesn't matter as long as it's not CGRectZero, otherwise a default empty
|
||||
// header is added by UITableView.
|
||||
FUIAuthTableHeaderView *tableHeaderView =
|
||||
[[FUIAuthTableHeaderView alloc] initWithFrame:_tableView.bounds];
|
||||
_tableView.tableHeaderView = tableHeaderView;
|
||||
[_forgotPasswordButton setTitle:FUILocalizedString(kStr_ForgotPasswordTitle)
|
||||
forState:UIControlStateNormal];
|
||||
_termsOfServiceView.authUI = self.authUI;
|
||||
[_termsOfServiceView useFooterMessage];
|
||||
[self enableDynamicCellHeightForTableView:_tableView];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
FUIAuthTableHeaderView *tableHeaderView =
|
||||
(FUIAuthTableHeaderView *)_tableView.tableHeaderView;
|
||||
tableHeaderView.titleLabel.text = FUILocalizedString(kStr_ExistingAccountTitle);
|
||||
tableHeaderView.detailLabel.text =
|
||||
[NSString stringWithFormat:FUILocalizedString(kStr_PasswordVerificationMessage), _email];
|
||||
|
||||
CGSize previousSize = tableHeaderView.frame.size;
|
||||
[tableHeaderView sizeToFit];
|
||||
if (!CGSizeEqualToSize(tableHeaderView.frame.size, previousSize)) {
|
||||
// Update the height of table header view by setting the view again.
|
||||
_tableView.tableHeaderView = tableHeaderView;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)next {
|
||||
[self verifyPassword:_passwordField.text];
|
||||
}
|
||||
|
||||
- (void)verifyPassword:(NSString *)password {
|
||||
if (![[self class] isValidEmail:_email]) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidEmailError)];
|
||||
return;
|
||||
}
|
||||
if (password.length <= 0) {
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_InvalidPasswordError)];
|
||||
return;
|
||||
}
|
||||
|
||||
[self incrementActivity];
|
||||
|
||||
FIRAuthCredential *credential =
|
||||
[FIREmailAuthProvider credentialWithEmail:_email password:password];
|
||||
[self.auth signInWithCredential:credential
|
||||
completion:^(FIRAuthDataResult *_Nullable authResult,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
[self decrementActivity];
|
||||
|
||||
[self showAlertWithMessage:FUILocalizedString(kStr_WrongPasswordError)];
|
||||
return;
|
||||
}
|
||||
|
||||
[authResult.user linkWithCredential:self->_newCredential
|
||||
completion:^(FIRAuthDataResult *_Nullable authResult,
|
||||
NSError *_Nullable error) {
|
||||
[self decrementActivity];
|
||||
|
||||
// Ignore any error (shouldn't happen) and treat the user as successfully signed in.
|
||||
[self dismissNavigationControllerAnimated:YES completion:^{
|
||||
[self.authUI invokeResultCallbackWithAuthDataResult:authResult URL:nil error:nil];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)forgotPassword {
|
||||
UIViewController *viewController;
|
||||
id<FUIAuthDelegate> delegate = self.authUI.delegate;
|
||||
|
||||
if ([delegate respondsToSelector:@selector(passwordRecoveryViewControllerForAuthUI:email:)]) {
|
||||
viewController = [delegate passwordRecoveryViewControllerForAuthUI:self.authUI
|
||||
email:_email];
|
||||
} else {
|
||||
viewController = [[FUIPasswordRecoveryViewController alloc] initWithAuthUI:self.authUI
|
||||
email:_email];
|
||||
}
|
||||
[self pushViewController:viewController];
|
||||
}
|
||||
|
||||
- (void)textFieldDidChange {
|
||||
[self didChangePassword:_passwordField.text];
|
||||
}
|
||||
|
||||
- (void)didChangePassword:(NSString *)password {
|
||||
BOOL enableActionButton = (password.length > 0);
|
||||
self.navigationItem.rightBarButtonItem.enabled = enableActionButton;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView
|
||||
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FUIAuthTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
if (!cell) {
|
||||
UINib *cellNib = [UINib nibWithNibName:NSStringFromClass([FUIAuthTableViewCell class])
|
||||
bundle:[FUIAuthUtils authUIBundle]];
|
||||
[tableView registerNib:cellNib forCellReuseIdentifier:kCellReuseIdentifier];
|
||||
cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
|
||||
}
|
||||
cell.textField.delegate = self;
|
||||
cell.label.text = FUILocalizedString(kStr_Password);
|
||||
_passwordField = cell.textField;
|
||||
_passwordField.placeholder = FUILocalizedString(kStr_EnterYourPassword);
|
||||
_passwordField.secureTextEntry = YES;
|
||||
_passwordField.returnKeyType = UIReturnKeyNext;
|
||||
_passwordField.keyboardType = UIKeyboardTypeDefault;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_passwordField.textContentType = UITextContentTypePassword;
|
||||
}
|
||||
[cell.textField addTarget:self
|
||||
action:@selector(textFieldDidChange)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
[self didChangePassword:_passwordField.text];
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||
if (textField == _passwordField) {
|
||||
[self next];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// 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 <UIKit/UIKit.h>
|
||||
|
||||
#import <FirebaseAuthUI/FUIAuthBaseViewController.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** @class FUIConfirmEmailViewController
|
||||
@brief The view controller that asks for user's email address.
|
||||
*/
|
||||
@interface FUIConfirmEmailViewController : FUIAuthBaseViewController
|
||||
|
||||
/** @fn onNext:
|
||||
@brief Should be called when user entered email. Triggers email verification before
|
||||
pushing new controller
|
||||
@param emailText Email value entered by user.
|
||||
*/
|
||||
- (void)onNext:(NSString *)emailText;
|
||||
|
||||
/** @fn didChangeEmail:
|
||||
@brief Update UI control state according to the email provided. Should be called after any
|
||||
change of email.
|
||||
@param emailText Email value entered by user.
|
||||
*/
|
||||
- (void)didChangeEmail:(NSString *)emailText;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
Generated
+129
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// 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 <FirebaseAuthUI/FirebaseAuthUI.h>
|
||||
|
||||
@class FUIAuth;
|
||||
@class FIRActionCodeSettings;
|
||||
@class FUIEmailEntryViewController;
|
||||
@class FUIPasswordSignInViewController;
|
||||
@class FUIPasswordSignUpViewController;
|
||||
@class FUIPasswordRecoveryViewController;
|
||||
@class FUIPasswordVerificationViewController;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** @class FUIEmailAuth
|
||||
@brief AuthUI components for Email Sign In.
|
||||
*/
|
||||
@interface FUIEmailAuth : NSObject <FUIAuthProvider>
|
||||
|
||||
/** @property emailLink.
|
||||
@brief The link for email link sign in.
|
||||
*/
|
||||
@property(nonatomic, strong, readwrite, nullable) NSString *emailLink;
|
||||
|
||||
/** @property buttonAlignment
|
||||
@brief The alignment of the icon and text of the button.
|
||||
*/
|
||||
@property(nonatomic, readwrite) FUIButtonAlignment buttonAlignment;
|
||||
|
||||
+ (NSBundle *)bundle;
|
||||
|
||||
/** @fn initAuthAuthUI:signInMethod:forceSameDevice:allowNewEmailAccounts:actionCodeSetting:
|
||||
@brief Initializer with several configurations.
|
||||
@param authUI The auth UI object that this auth UI provider associate with.
|
||||
@param signInMethod The email sign in method, which can be password or email link.
|
||||
@param forceSameDevice Indicate whether for the email sign in link to be open on the same device.
|
||||
@param allowNewEmailAccounts Indicate whether allow sign up if the user doesn't exist.
|
||||
@param actionCodeSettings The action code settings for email actions.
|
||||
*/
|
||||
- (instancetype)initAuthAuthUI:(FUIAuth *)authUI
|
||||
signInMethod:(NSString *)signInMethod
|
||||
forceSameDevice:(BOOL)forceSameDevice
|
||||
allowNewEmailAccounts:(BOOL)allowNewEmailAccounts
|
||||
actionCodeSetting:(FIRActionCodeSettings *)actionCodeSettings;
|
||||
|
||||
/** @fn initAuthAuthUI:signInMethod:forceSameDevice:allowNewEmailAccounts:requireDisplayName:actionCodeSetting:
|
||||
@brief Initializer with several configurations.
|
||||
@param authUI The auth UI object that this auth UI provider associate with.
|
||||
@param signInMethod The email sign in method, which can be password or email link.
|
||||
@param forceSameDevice Indicate whether for the email sign in link to be open on the same device.
|
||||
@param allowNewEmailAccounts Indicate whether allow sign up if the user doesn't exist.
|
||||
@param requireDisplayName Indicate whether require display name when sign up.
|
||||
@param actionCodeSettings The action code settings for email actions.
|
||||
*/
|
||||
- (instancetype)initAuthAuthUI:(FUIAuth *)authUI
|
||||
signInMethod:(NSString *)signInMethod
|
||||
forceSameDevice:(BOOL)forceSameDevice
|
||||
allowNewEmailAccounts:(BOOL)allowNewEmailAccounts
|
||||
requireDisplayName:(BOOL)requireDisplayName
|
||||
actionCodeSetting:(FIRActionCodeSettings *)actionCodeSettings;
|
||||
|
||||
/** @property signInMethod.
|
||||
@brief Defines the sign in method for FIREmailAuthProvider.
|
||||
This can be one of the following string constants:
|
||||
- FIREmailLinkAuthSignInMethod
|
||||
- FIREmailPasswordAuthSignInMethod (default).
|
||||
*/
|
||||
@property(nonatomic, copy, readonly) NSString *signInMethod;
|
||||
|
||||
/** @property forceSameDevice.
|
||||
@brief Whether to force same device flow. If not, opening the link on a different device will
|
||||
display an error message. Note that this should be true when used with anonymous user
|
||||
upgrade flows. The default is false.
|
||||
*/
|
||||
@property(nonatomic, assign, readonly) BOOL forceSameDevice;
|
||||
|
||||
/** @property actionCodeSettings.
|
||||
@brief Defines the FIRActionCodeSettings configuration to use when sending the link. This gives
|
||||
the developer the ability to specify how the link can be handled, custom dynamic link,
|
||||
additional state in the deep link, etc.
|
||||
*/
|
||||
@property(nonatomic, strong, readonly) FIRActionCodeSettings *actionCodeSettings;
|
||||
|
||||
/** @property allowNewEmailAccounts
|
||||
@brief Whether to allow new user sign, defaults to YES.
|
||||
*/
|
||||
@property(nonatomic, assign, readonly) BOOL allowNewEmailAccounts;
|
||||
|
||||
/** @property requireDisplayName
|
||||
@brief Whether signup requires display name, defaults to YES.
|
||||
*/
|
||||
@property(nonatomic, assign, readonly) BOOL requireDisplayName;
|
||||
|
||||
/** @fn signInWithPresentingViewController:
|
||||
@brief Signs in with email auth provider.
|
||||
@see FUIAuthDelegate.authUI:didSignInWithAuthDataResult:URL:error: for method callback.
|
||||
@param presentingViewController The view controller used to present the UI.
|
||||
*/
|
||||
- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
|
||||
__attribute__((deprecated("This is deprecated API and will be removed in a future release."
|
||||
"Please use signInWithPresentingViewController:email:")));
|
||||
|
||||
/** @fn signInWithPresentingViewController:email:
|
||||
@brief Signs in with email auth provider.
|
||||
@see FUIAuthDelegate.authUI:didSignInWithAuthDataResult:URL:error: for method callback.
|
||||
@param presentingViewController The view controller used to present the UI.
|
||||
@param email The default email address.
|
||||
*/
|
||||
- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
|
||||
email:(nullable NSString *)email;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 <UIKit/UIKit.h>
|
||||
|
||||
#import <FirebaseAuthUI/FUIAuthBaseViewController.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** @class FUIEmailEntryViewController
|
||||
@brief The view controller that asks for user's email address.
|
||||
*/
|
||||
@interface FUIEmailEntryViewController : FUIAuthBaseViewController
|
||||
|
||||
/** @fn onNext:
|
||||
@brief Should be called when user entered email. Triggers email verification before
|
||||
pushing new controller
|
||||
@param emailText Email value entered by user.
|
||||
*/
|
||||
- (void)onNext:(NSString *)emailText;
|
||||
|
||||
/** @fn didChangeEmail:
|
||||
@brief Should be called after any change of email value. Updates UI controls state
|
||||
(e g state of next button)
|
||||
@param emailText Email value entered by user.
|
||||
*/
|
||||
- (void)didChangeEmail:(NSString *)emailText;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 <UIKit/UIKit.h>
|
||||
|
||||
#import <FirebaseAuthUI/FUIAuthBaseViewController.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** @class FUIPasswordRecoveryViewController
|
||||
@brief The view controller that asks for user's password.
|
||||
*/
|
||||
@interface FUIPasswordRecoveryViewController : FUIAuthBaseViewController
|
||||
|
||||
/** @fn initWithNibName:bundle:authUI:
|
||||
@brief Please use @c initWithAuthUI:email:.
|
||||
*/
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI NS_UNAVAILABLE;
|
||||
|
||||
/** @fn initWithAuthUI:
|
||||
@brief Please use @c initWithAuthUI:email:.
|
||||
*/
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI NS_UNAVAILABLE;
|
||||
|
||||
/** @fn initWithNibName:bundle:authUI:email:
|
||||
@brief Designated initializer.
|
||||
@param nibNameOrNil The name of the nib file to associate with the view controller.
|
||||
@param nibBundleOrNil The bundle in which to search for the nib file.
|
||||
@param authUI The @c FUIAuth instance that manages this view controller.
|
||||
@param email The email address of the user.
|
||||
*/
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/** @fn initWithAuthUI:email:
|
||||
@brief Convenience initializer.
|
||||
@param authUI The @c FUIAuth instance that manages this view controller.
|
||||
@param email The email address of the user.
|
||||
*/
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email;
|
||||
|
||||
/** @fn didChangeEmail:
|
||||
@brief Should be called after any change of email value. Updates UI controls state
|
||||
(e g state of send button)
|
||||
@param email The email address of the user.
|
||||
*/
|
||||
- (void)didChangeEmail:(NSString *)email;
|
||||
|
||||
/** @fn recoverEmail:
|
||||
@brief Should be called when user want to recover password for specified email.
|
||||
Sends email recover request.
|
||||
@param email The email address of the user.
|
||||
*/
|
||||
- (void)recoverEmail:(NSString *)email;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 <UIKit/UIKit.h>
|
||||
|
||||
#import <FirebaseAuthUI/FUIAuthBaseViewController.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** @class FUIPasswordSignInViewController
|
||||
@brief The view controller that asks for user's password.
|
||||
*/
|
||||
@interface FUIPasswordSignInViewController : FUIAuthBaseViewController
|
||||
|
||||
/** @fn initWithNibName:bundle:authUI:
|
||||
@brief Please use @c initWithNibName:bundle:authUI:email:.
|
||||
*/
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI NS_UNAVAILABLE;
|
||||
|
||||
/** @fn initWithAuthUI:
|
||||
@brief Please use @c initWithNibName:bundle:authUI:email:.
|
||||
*/
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI NS_UNAVAILABLE;
|
||||
|
||||
/** @fn initWithNibName:bundle:authUI:email:
|
||||
@brief Designated initializer.
|
||||
@param nibNameOrNil The name of the nib file to associate with the view controller.
|
||||
@param nibBundleOrNil The bundle in which to search for the nib file.
|
||||
@param authUI The @c FUIAuth instance that manages this view controller.
|
||||
@param email The email address of the user.
|
||||
*/
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/** @fn initWithAuthUI:email:
|
||||
@brief Convenience initializer.
|
||||
@param authUI The @c FUIAuth instance that manages this view controller.
|
||||
@param email The email address of the user.
|
||||
*/
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email;
|
||||
|
||||
/** @fn forgotPasswordForEmail:
|
||||
@brief Method is called when user forgot password.
|
||||
@param email The email address of the user.
|
||||
*/
|
||||
- (void)forgotPasswordForEmail:(NSString *)email;
|
||||
|
||||
/** @fn didChangeEmail:andPassword:
|
||||
@brief Should be called after any change of email/password value. Updates UI controls state
|
||||
(e g state of next button)
|
||||
@param email The email address of the user.
|
||||
@param password The password which user uses.
|
||||
*/
|
||||
- (void)didChangeEmail:(NSString *)email andPassword:(NSString *)password;
|
||||
|
||||
/** @fn signInWithDefaultValue:andPassword:
|
||||
@brief Should be called when user entered credentials. Sends authorization request
|
||||
@param email The email address of the user.
|
||||
@param password The password which user uses.
|
||||
*/
|
||||
- (void)signInWithDefaultValue:(NSString *)email andPassword:(NSString *)password;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 <UIKit/UIKit.h>
|
||||
|
||||
#import <FirebaseAuthUI/FUIAuthBaseViewController.h>
|
||||
|
||||
@class FUIPrivacyAndTermsOfServiceView;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** @class FUIPasswordSignUpViewController
|
||||
@brief The view controller where user signs up as a password account.
|
||||
*/
|
||||
@interface FUIPasswordSignUpViewController : FUIAuthBaseViewController
|
||||
|
||||
/** @property footerTextView
|
||||
@brief The view in the footer of the table that displays Privacy and Terms of Service.
|
||||
*/
|
||||
@property(nonatomic, strong) IBOutlet FUIPrivacyAndTermsOfServiceView *footerView;
|
||||
|
||||
/** @fn initWithNibName:bundle:authUI:
|
||||
@brief Please use @c initWithAuthUI:email:.
|
||||
*/
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI NS_UNAVAILABLE;
|
||||
|
||||
/** @fn initWithAuthUI:
|
||||
@brief Please use @c initWithAuthUI:email:.
|
||||
*/
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI NS_UNAVAILABLE;
|
||||
|
||||
/** @fn initWithNibName:bundle:authUI:email:
|
||||
@brief Designated initializer.
|
||||
@param nibNameOrNil The name of the nib file to associate with the view controller.
|
||||
@param nibBundleOrNil The bundle in which to search for the nib file.
|
||||
@param authUI The @c FUIAuth instance that manages this view controller.
|
||||
@param email The email address of the user.
|
||||
@param requireDisplayName Whether the displayname field is required .
|
||||
*/
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email
|
||||
requireDisplayName:(BOOL)requireDisplayName NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/** @fn initWithAuthUI:email:
|
||||
@brief Convenience initializer.
|
||||
@param authUI The @c FUIAuth instance that manages this view controller.
|
||||
@param email The email address of the user.
|
||||
@param requireDisplayName Whether the displayname field is required .
|
||||
*/
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email
|
||||
requireDisplayName:(BOOL)requireDisplayName;
|
||||
|
||||
/** @fn didChangeEmail:orPassword:orUserName:
|
||||
@brief Should be called after any change of email, password or user name value.
|
||||
Updates UI controls state (e g state of next button)
|
||||
@param email The email address of the user.
|
||||
@param password The password which user uses.
|
||||
@param username The username which user uses.
|
||||
*/
|
||||
- (void)didChangeEmail:(NSString *)email
|
||||
orPassword:(NSString *)password
|
||||
orUserName:(NSString *)username;
|
||||
|
||||
/** @fn signUpWithEmail:andPassword:andUsername:
|
||||
@brief Should be called when user entered credentials and name. Sends request to create
|
||||
new user and second request to update it's name
|
||||
@param email The email address of the user.
|
||||
@param password The password which user uses.
|
||||
@param username The username which user uses.
|
||||
*/
|
||||
- (void)signUpWithEmail:(NSString *)email
|
||||
andPassword:(NSString *)password
|
||||
andUsername:(NSString *)username;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 <UIKit/UIKit.h>
|
||||
|
||||
#import <FirebaseAuthUI/FUIAuthBaseViewController.h>
|
||||
|
||||
@class FIRAuthCredential;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** @class FUIPasswordVerificationViewController
|
||||
@brief The view controller that verifies user's password.
|
||||
*/
|
||||
@interface FUIPasswordVerificationViewController : FUIAuthBaseViewController
|
||||
|
||||
/** @fn initWithNibName:bundle:authUI:
|
||||
@brief Please use @c initWithAuthUI:email:.
|
||||
*/
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI NS_UNAVAILABLE;
|
||||
|
||||
/** @fn initWithAuthUI:
|
||||
@brief Please use @c initWithAuthUI:email:.
|
||||
*/
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI NS_UNAVAILABLE;
|
||||
|
||||
/** @fn initWithNibName:bundle:authUI:email:newCredential:
|
||||
@brief Designated initializer.
|
||||
@param nibNameOrNil The name of the nib file to associate with the view controller.
|
||||
@param nibBundleOrNil The bundle in which to search for the nib file.
|
||||
@param authUI The @c FUIAuth instance that manages this view controller.
|
||||
@param email The email address of the user.
|
||||
@param newCredential The new @c FIRAuthCredential that the user had never used before.
|
||||
*/
|
||||
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
authUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email
|
||||
newCredential:(FIRAuthCredential *)newCredential NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/** @fn initWithAuthUI:email:newCredential:
|
||||
@brief Convenience initializer.
|
||||
@param authUI The @c FUIAuth instance that manages this view controller.
|
||||
@param email The email address of the user.
|
||||
@param newCredential The new @c FIRAuthCredential that the user had never used before.
|
||||
*/
|
||||
- (instancetype)initWithAuthUI:(FUIAuth *)authUI
|
||||
email:(NSString *_Nullable)email
|
||||
newCredential:(FIRAuthCredential *)newCredential;
|
||||
|
||||
/** @fn forgotPassword
|
||||
@brief Method is called when user forgot password.
|
||||
*/
|
||||
- (void)forgotPassword;
|
||||
|
||||
/** @fn didChangePassword:
|
||||
@brief Should be called after any change of password value. Updates UI controls state
|
||||
(e g state of next button)
|
||||
@param password The password which user uses.
|
||||
*/
|
||||
- (void)didChangePassword:(NSString *)password;
|
||||
|
||||
/** @fn verifyPassword:
|
||||
@brief Should be called when user entered password. Sends authorization request
|
||||
@param password The password which user uses.
|
||||
*/
|
||||
- (void)verifyPassword:(NSString *)password;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) 2016 Google Inc.
|
||||
//
|
||||
// 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 <UIKit/UIKit.h>
|
||||
|
||||
//! Project version number for FirebaseEmailAuthUI.
|
||||
FOUNDATION_EXPORT double FirebaseEmailAuthUIVersionNumber;
|
||||
|
||||
//! Project version string for FirebaseEmailAuthUI.
|
||||
FOUNDATION_EXPORT const unsigned char FirebaseEmailAuthUIVersionString[];
|
||||
|
||||
#import "FUIEmailAuth.h"
|
||||
#import "FUIEmailEntryViewController.h"
|
||||
#import "FUIPasswordRecoveryViewController.h"
|
||||
#import "FUIPasswordVerificationViewController.h"
|
||||
#import "FUIPasswordSignInViewController.h"
|
||||
#import "FUIPasswordSignUpViewController.h"
|
||||
Generated
+62
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FUIConfirmEmailViewController">
|
||||
<connections>
|
||||
<outlet property="_tableView" destination="sqQ-Fo-hB1" id="5Ek-qF-gTL"/>
|
||||
<outlet property="_termsOfServiceView" destination="9ea-qX-Sdg" id="T20-ZO-fz9"/>
|
||||
<outlet property="view" destination="bth-CL-c9r" id="F2l-k9-ffn"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="bth-CL-c9r">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="sqQ-Fo-hB1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="zAD-Xd-4qt">
|
||||
<rect key="frame" x="0.0" y="896.5" width="375" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" text="Terms of Service" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9ea-qX-Sdg" customClass="FUIPrivacyAndTermsOfServiceView">
|
||||
<rect key="frame" x="10" y="0.0" width="355" height="100"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="9ea-qX-Sdg" secondAttribute="trailing" constant="10" id="XIO-RK-VZ7"/>
|
||||
<constraint firstItem="9ea-qX-Sdg" firstAttribute="top" secondItem="zAD-Xd-4qt" secondAttribute="top" id="b5A-Zf-YCc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="9ea-qX-Sdg" secondAttribute="bottom" id="iuk-lu-1BT"/>
|
||||
<constraint firstItem="9ea-qX-Sdg" firstAttribute="leading" secondItem="zAD-Xd-4qt" secondAttribute="leading" constant="10" id="lMZ-cS-2hg"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="-1" id="Sw5-oz-cWe"/>
|
||||
<outlet property="delegate" destination="-1" id="xr0-I0-wCb"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="sqQ-Fo-hB1" secondAttribute="trailing" id="5Me-cs-Zxv"/>
|
||||
<constraint firstItem="sqQ-Fo-hB1" firstAttribute="top" secondItem="bth-CL-c9r" secondAttribute="top" id="OYw-Zf-Kez"/>
|
||||
<constraint firstAttribute="bottom" secondItem="sqQ-Fo-hB1" secondAttribute="bottom" id="c4f-G4-8CC"/>
|
||||
<constraint firstItem="sqQ-Fo-hB1" firstAttribute="leading" secondItem="bth-CL-c9r" secondAttribute="leading" id="y2b-6O-eFn"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
Generated
+62
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FUIEmailEntryViewController">
|
||||
<connections>
|
||||
<outlet property="_tableView" destination="sqQ-Fo-hB1" id="5Ek-qF-gTL"/>
|
||||
<outlet property="_termsOfServiceView" destination="9ea-qX-Sdg" id="T20-ZO-fz9"/>
|
||||
<outlet property="view" destination="bth-CL-c9r" id="F2l-k9-ffn"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="bth-CL-c9r">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="sqQ-Fo-hB1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="zAD-Xd-4qt">
|
||||
<rect key="frame" x="0.0" y="896.5" width="375" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" text="Terms of Service" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="9ea-qX-Sdg" customClass="FUIPrivacyAndTermsOfServiceView">
|
||||
<rect key="frame" x="10" y="0.0" width="355" height="100"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="9ea-qX-Sdg" secondAttribute="trailing" constant="10" id="XIO-RK-VZ7"/>
|
||||
<constraint firstItem="9ea-qX-Sdg" firstAttribute="top" secondItem="zAD-Xd-4qt" secondAttribute="top" id="b5A-Zf-YCc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="9ea-qX-Sdg" secondAttribute="bottom" id="iuk-lu-1BT"/>
|
||||
<constraint firstItem="9ea-qX-Sdg" firstAttribute="leading" secondItem="zAD-Xd-4qt" secondAttribute="leading" constant="10" id="lMZ-cS-2hg"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="-1" id="Sw5-oz-cWe"/>
|
||||
<outlet property="delegate" destination="-1" id="xr0-I0-wCb"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="sqQ-Fo-hB1" secondAttribute="trailing" id="5Me-cs-Zxv"/>
|
||||
<constraint firstItem="sqQ-Fo-hB1" firstAttribute="top" secondItem="bth-CL-c9r" secondAttribute="top" id="OYw-Zf-Kez"/>
|
||||
<constraint firstAttribute="bottom" secondItem="sqQ-Fo-hB1" secondAttribute="bottom" id="c4f-G4-8CC"/>
|
||||
<constraint firstItem="sqQ-Fo-hB1" firstAttribute="leading" secondItem="bth-CL-c9r" secondAttribute="leading" id="y2b-6O-eFn"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
Pods/FirebaseEmailAuthUI/FirebaseEmailAuthUI/Sources/Resources/FUIPasswordRecoveryViewController.xib
Generated
+74
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FUIPasswordRecoveryViewController">
|
||||
<connections>
|
||||
<outlet property="_tableView" destination="H6t-i3-acV" id="0wN-sA-gSw"/>
|
||||
<outlet property="footerTextView" destination="ncz-Ir-7MQ" id="35L-yA-7Du"/>
|
||||
<outlet property="termsOfServiceView" destination="3ap-1R-mtP" id="Ufh-bn-MRb"/>
|
||||
<outlet property="view" destination="dVE-hn-rd9" id="maO-pT-zcy"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="dVE-hn-rd9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="H6t-i3-acV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="hh3-Pu-5aV">
|
||||
<rect key="frame" x="0.0" y="896.5" width="375" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" verticalHuggingPriority="249" bounces="NO" scrollEnabled="NO" editable="NO" text="Get instructions sent to this email that explain how to reset your password." textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ncz-Ir-7MQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="45"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" editable="NO" text="Terms of Service" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="3ap-1R-mtP" userLabel="Terms Of Service View" customClass="FUIPrivacyAndTermsOfServiceView">
|
||||
<rect key="frame" x="0.0" y="69" width="375" height="30.5"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="3ap-1R-mtP" secondAttribute="trailing" id="0xr-oA-YXj"/>
|
||||
<constraint firstItem="3ap-1R-mtP" firstAttribute="top" secondItem="ncz-Ir-7MQ" secondAttribute="bottom" constant="24" id="BRj-Ou-eVU" userLabel="Terms Of Service View.top = Footer Text View.bottom"/>
|
||||
<constraint firstAttribute="bottom" secondItem="3ap-1R-mtP" secondAttribute="bottom" constant="0.5" id="MM3-sc-mhf"/>
|
||||
<constraint firstItem="3ap-1R-mtP" firstAttribute="leading" secondItem="hh3-Pu-5aV" secondAttribute="leading" id="k7B-5m-MiU"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ncz-Ir-7MQ" secondAttribute="trailing" id="ob3-Jw-8iF"/>
|
||||
<constraint firstItem="ncz-Ir-7MQ" firstAttribute="top" secondItem="hh3-Pu-5aV" secondAttribute="top" id="tWd-CY-6tT"/>
|
||||
<constraint firstItem="ncz-Ir-7MQ" firstAttribute="leading" secondItem="hh3-Pu-5aV" secondAttribute="leading" id="xyd-cq-b53"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="-1" id="p7d-oK-2Dk"/>
|
||||
<outlet property="delegate" destination="-1" id="nqi-Zo-4EP"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="centerY" secondItem="dVE-hn-rd9" secondAttribute="centerY" id="MFP-Pv-kCs"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="centerX" secondItem="dVE-hn-rd9" secondAttribute="centerX" id="Uhd-9T-8Yp"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="width" secondItem="dVE-hn-rd9" secondAttribute="width" id="clA-Wq-s6e"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="height" secondItem="dVE-hn-rd9" secondAttribute="height" id="rr8-sc-2Hl"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="24.5" y="51.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
Generated
+74
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FUIPasswordSignInViewController">
|
||||
<connections>
|
||||
<outlet property="_forgotPasswordButton" destination="6Y9-ZQ-dJB" id="olQ-ub-jJE"/>
|
||||
<outlet property="_tableView" destination="H6t-i3-acV" id="2yw-RV-Ysr"/>
|
||||
<outlet property="_termsOfServiceView" destination="aQs-HQ-6lQ" id="JF4-Nd-I2m"/>
|
||||
<outlet property="view" destination="aGN-ql-cJM" id="ZBG-OT-2iz"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="aGN-ql-cJM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="H6t-i3-acV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="OJW-RE-fac">
|
||||
<rect key="frame" x="0.0" y="896.5" width="375" height="76"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="top" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6Y9-ZQ-dJB">
|
||||
<rect key="frame" x="0.0" y="15" width="375" height="27"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<state key="normal" title="Trouble signing in?"/>
|
||||
<connections>
|
||||
<action selector="forgotPassword" destination="-1" eventType="touchUpInside" id="a0I-G6-f7v"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" verticalCompressionResistancePriority="1000" editable="NO" text="Terms of Service" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="aQs-HQ-6lQ" customClass="FUIPrivacyAndTermsOfServiceView">
|
||||
<rect key="frame" x="0.0" y="49" width="375" height="27"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="6Y9-ZQ-dJB" secondAttribute="trailing" id="9wq-Av-A0o" userLabel="trailing = Forgot Password Button.trailing"/>
|
||||
<constraint firstItem="aQs-HQ-6lQ" firstAttribute="leading" secondItem="OJW-RE-fac" secondAttribute="leading" id="NMe-Cu-8QK"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aQs-HQ-6lQ" secondAttribute="trailing" id="Oal-8w-YP8"/>
|
||||
<constraint firstItem="6Y9-ZQ-dJB" firstAttribute="top" secondItem="OJW-RE-fac" secondAttribute="top" constant="15" id="Rix-Qr-wa7" userLabel="Forgot Password Button.top = top"/>
|
||||
<constraint firstItem="6Y9-ZQ-dJB" firstAttribute="leading" secondItem="OJW-RE-fac" secondAttribute="leading" id="SV1-7o-b14" userLabel="Forgot Password Button.leading = leading"/>
|
||||
<constraint firstItem="aQs-HQ-6lQ" firstAttribute="top" secondItem="6Y9-ZQ-dJB" secondAttribute="bottom" constant="7" id="djY-3a-0BM" userLabel="Terms Of Service View.top = Forgot Password Button.bottom"/>
|
||||
<constraint firstAttribute="bottom" secondItem="aQs-HQ-6lQ" secondAttribute="bottom" id="kJm-07-Pxy"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="-1" id="KV3-Lu-Ggx"/>
|
||||
<outlet property="delegate" destination="-1" id="moM-8u-6m7"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="width" secondItem="aGN-ql-cJM" secondAttribute="width" id="1gY-LP-2Wt"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="centerY" secondItem="aGN-ql-cJM" secondAttribute="centerY" id="NIa-qK-fiA"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="height" secondItem="aGN-ql-cJM" secondAttribute="height" id="l6X-P4-TGO"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="centerX" secondItem="aGN-ql-cJM" secondAttribute="centerX" id="w7a-K0-SPa"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="24.5" y="51.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
Generated
+64
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FUIPasswordSignUpViewController">
|
||||
<connections>
|
||||
<outlet property="_tableView" destination="H6t-i3-acV" id="aQc-w1-k00"/>
|
||||
<outlet property="footerView" destination="MCr-Ut-Fly" id="akL-cK-5B8"/>
|
||||
<outlet property="view" destination="S20-6W-lc7" id="AOO-4u-Ua6"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="S20-6W-lc7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="H6t-i3-acV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="uep-ps-dg1">
|
||||
<rect key="frame" x="0.0" y="896.5" width="375" height="65"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" editable="NO" text="By tapping Next you are indicating that you agree to the Terms of Service." textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="MCr-Ut-Fly" userLabel="Footer View" customClass="FUIPrivacyAndTermsOfServiceView">
|
||||
<rect key="frame" x="8" y="0.0" width="359" height="65"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<dataDetectorType key="dataDetectorTypes" link="YES"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="MCr-Ut-Fly" secondAttribute="bottom" id="IoY-D9-TS4"/>
|
||||
<constraint firstItem="MCr-Ut-Fly" firstAttribute="top" secondItem="uep-ps-dg1" secondAttribute="top" id="XNM-WX-bZc"/>
|
||||
<constraint firstAttribute="trailing" secondItem="MCr-Ut-Fly" secondAttribute="trailing" constant="8" id="Y3o-Hh-NIN"/>
|
||||
<constraint firstItem="MCr-Ut-Fly" firstAttribute="leading" secondItem="uep-ps-dg1" secondAttribute="leading" constant="8" id="rgh-ee-glq"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="-1" id="tM9-3Y-9eD"/>
|
||||
<outlet property="delegate" destination="-1" id="pwa-PP-ckY"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="width" secondItem="S20-6W-lc7" secondAttribute="width" id="V5B-Zv-EBd"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="height" secondItem="S20-6W-lc7" secondAttribute="height" id="oI4-11-EsK"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="centerY" secondItem="S20-6W-lc7" secondAttribute="centerY" id="qcO-oY-gb2"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="centerX" secondItem="S20-6W-lc7" secondAttribute="centerX" id="w9G-qb-voz"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="24.5" y="51.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FUIPasswordVerificationViewController">
|
||||
<connections>
|
||||
<outlet property="_forgotPasswordButton" destination="6Y9-ZQ-dJB" id="9Rv-sT-Cqk"/>
|
||||
<outlet property="_tableView" destination="H6t-i3-acV" id="oCK-Bh-qFC"/>
|
||||
<outlet property="_termsOfServiceView" destination="RVd-Da-OmM" id="n4e-fk-y08"/>
|
||||
<outlet property="view" destination="gVk-tK-d7p" id="9OM-dU-Zll"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="gVk-tK-d7p">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="H6t-i3-acV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="8hY-ak-fhD">
|
||||
<rect key="frame" x="0.0" y="896.5" width="375" height="56"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6Y9-ZQ-dJB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="15"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<inset key="contentEdgeInsets" minX="8" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" title="Trouble signing in?"/>
|
||||
<connections>
|
||||
<action selector="forgotPassword" destination="-1" eventType="touchUpInside" id="a0I-G6-f7v"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" text="Terms of Service" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="RVd-Da-OmM" userLabel="Terms of Service View" customClass="FUIPrivacyAndTermsOfServiceView">
|
||||
<rect key="frame" x="0.0" y="23" width="375" height="33"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="6Y9-ZQ-dJB" firstAttribute="top" secondItem="8hY-ak-fhD" secondAttribute="top" id="0xY-d2-cSm"/>
|
||||
<constraint firstItem="RVd-Da-OmM" firstAttribute="leading" secondItem="8hY-ak-fhD" secondAttribute="leading" id="7Wt-8v-7kg"/>
|
||||
<constraint firstItem="6Y9-ZQ-dJB" firstAttribute="leading" secondItem="8hY-ak-fhD" secondAttribute="leading" id="Eni-US-s1T"/>
|
||||
<constraint firstAttribute="trailing" secondItem="6Y9-ZQ-dJB" secondAttribute="trailing" id="U6c-DG-Uvv"/>
|
||||
<constraint firstAttribute="bottom" secondItem="RVd-Da-OmM" secondAttribute="bottom" id="fCN-24-cWy"/>
|
||||
<constraint firstItem="RVd-Da-OmM" firstAttribute="top" secondItem="6Y9-ZQ-dJB" secondAttribute="bottom" constant="8" id="kyG-kU-bHe" userLabel="Terms of Service View.top = Forgot Password Button.bottom"/>
|
||||
<constraint firstAttribute="trailing" secondItem="RVd-Da-OmM" secondAttribute="trailing" id="s2N-m1-EYU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="-1" id="KV3-Lu-Ggx"/>
|
||||
<outlet property="delegate" destination="-1" id="moM-8u-6m7"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="width" secondItem="gVk-tK-d7p" secondAttribute="width" id="2PO-iD-kw3"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="centerX" secondItem="gVk-tK-d7p" secondAttribute="centerX" id="8Nm-yf-f9N"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="centerY" secondItem="gVk-tK-d7p" secondAttribute="centerY" id="klS-EG-4Xf"/>
|
||||
<constraint firstItem="H6t-i3-acV" firstAttribute="height" secondItem="gVk-tK-d7p" secondAttribute="height" id="pfI-Kt-yTa"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="24.5" y="51.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 228 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 351 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 493 B |
Generated
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
Generated
+156
@@ -0,0 +1,156 @@
|
||||
# FirebaseUI for iOS — UI Bindings for Firebase
|
||||
|
||||
          
|
||||
|
||||
FirebaseUI is an open-source library for iOS that allows you to quickly connect common UI elements to the [Firebase](https://firebase.google.com?utm_source=FirebaseUI-iOS) database for data storage, allowing views to be updated in realtime as they change, and providing simple interfaces for common tasks like displaying lists or collections of items.
|
||||
|
||||
Additionally, FirebaseUI simplifies Firebase authentication by providing easy to use auth methods that integrate with common identity providers like Facebook, Twitter, and Google as well as allowing developers to use a built in headful UI for ease of development.
|
||||
|
||||
FirebaseUI clients are also available for [Android](https://github.com/firebase/FirebaseUI-Android) and [web](https://github.com/firebase/firebaseui-web).
|
||||
|
||||

|
||||
|
||||
## Installing FirebaseUI for iOS
|
||||
|
||||
FirebaseUI supports iOS 10.0+ and Xcode 11+. We recommend using [CocoaPods](https://cocoapods.org/pods/FirebaseUI), add
|
||||
the following to your `Podfile`:
|
||||
|
||||
```ruby
|
||||
pod 'FirebaseUI', '~> 8.0' # Pull in all Firebase UI features
|
||||
```
|
||||
|
||||
If you don't want to use all of FirebaseUI, there are multiple subspecs which can selectively install subsets of the full feature set:
|
||||
|
||||
```ruby
|
||||
# Only pull in Firestore features
|
||||
pod 'FirebaseUI/Firestore', '~> 8.0'
|
||||
|
||||
# Only pull in Database features
|
||||
pod 'FirebaseUI/Database', '~> 8.0'
|
||||
|
||||
# Only pull in Storage features
|
||||
pod 'FirebaseUI/Storage', '~> 8.0'
|
||||
|
||||
# Only pull in Auth features
|
||||
pod 'FirebaseUI/Auth', '~> 8.0'
|
||||
|
||||
# Only pull in Facebook login features
|
||||
pod 'FirebaseUI/Facebook', '~> 8.0'
|
||||
|
||||
# Only pull in Google login features
|
||||
pod 'FirebaseUI/Google', '~> 8.0'
|
||||
|
||||
# Only pull in Phone Auth login features
|
||||
pod 'FirebaseUI/Phone', '~> 8.0'
|
||||
```
|
||||
|
||||
If you're including FirebaseUI in a Swift project, make sure you also have:
|
||||
|
||||
```ruby
|
||||
platform :ios, '10.0'
|
||||
use_frameworks!
|
||||
```
|
||||
|
||||
Otherwise, you can include the FirebaseUI Xcode project from this repo in
|
||||
your project. You also need to
|
||||
[add the Firebase framework](https://firebase.google.com/docs/ios/setup)
|
||||
to your project.
|
||||
|
||||
## Documentation
|
||||
|
||||
The READMEs for components of FirebaseUI can be found in their respective
|
||||
project folders.
|
||||
|
||||
- [Auth](Auth/README.md)
|
||||
- [PhoneAuth](PhoneAuth/README.md)
|
||||
- [Database](Database/README.md)
|
||||
- [Firestore](Firestore/README.md)
|
||||
- [Storage](Storage/README.md)
|
||||
|
||||
## Local Setup
|
||||
|
||||
If you'd like to contribute to FirebaseUI for iOS, you'll need to run the
|
||||
following commands to get your environment set up:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/firebase/FirebaseUI-iOS.git
|
||||
$ cd FirebaseUI-iOS
|
||||
$ cd Auth # or PhoneAuth, Database, etc
|
||||
$ pod install
|
||||
```
|
||||
|
||||
Alternatively you can use `pod try FirebaseUI` to install the Objective-C or Swift sample projects.
|
||||
|
||||
## Sample Project Configuration
|
||||
|
||||
You'll have to configure your Xcode project in order to run the samples.
|
||||
|
||||
1. Your Xcode project should contain a `GoogleService-Info.plist`, downloaded from [Firebase console](https://console.firebase.google.com) when you add your app to a Firebase project.<br>
|
||||
Copy the `GoogleService-Info.plist` into the sample project folder (`samples/obj-c/GoogleService-Info.plist` or `samples/swift/GoogleService-Info.plist`).
|
||||
|
||||
1. Update URL Types.<br>
|
||||
Go to `Project Settings -> Info tab -> Url Types` and update values for:
|
||||
+ `REVERSED_CLIENT_ID` (get value from `GoogleService-Info.plist`)
|
||||
+ `fb{your-app-id}` (put Facebook App Id)
|
||||
|
||||
1. Update `Info.plist` with Facebook configuration values
|
||||
+ `FacebookAppID -> {your-app-id}` (put Facebook App Id)
|
||||
|
||||
1. Enable Keychain Sharing.<br>
|
||||
Facebook SDK requires keychain sharing.<br>
|
||||
This can be done here: `Project Settings -> Capabilities -> KeyChain Sharing -> ON`
|
||||
|
||||
1. Don't forget to configure your Firebase App Database using [Firebase console](https://console.firebase.google.com).<br>
|
||||
Database should contain appropriate read/write permissions and folders (`objc_demo-chat` and `swift_demo-chat` respectively)
|
||||
|
||||
1. In Order to use `Phone Auth` provider you should [Configure Push Notifications](#configure-apple-push-notifications)
|
||||
|
||||
#### Configure Apple Push Notifications
|
||||
|
||||
##### Enable silent push notifications in Xcode
|
||||
|
||||
* `Push Notification` - Under `Capabilities` tab in your app target choose `Push Notifications` and put the switch to the `On` position.
|
||||
* `Background Mode` - Under `Capabilities` tab in your app target choose `Background Modes` put the switch to the `On` position. In the list of available modes select `Background fetch` and `Remote notifications` (If available).
|
||||
|
||||
##### Upload APNS Certificate to Firebase
|
||||
|
||||
1. Create your `Provisioning APNS SSL Certificates` by following the steps on the following link.
|
||||
https://firebase.google.com/docs/cloud-messaging/ios/certs
|
||||
|
||||
1. Upload your `APNS Certificate` to Firebase:
|
||||
+ Inside your project in the Firebase console, select the gear icon, select `Project Settings`, and then select the `Cloud Messaging` tab.
|
||||
+ Select the `Upload Certificate` button for your development certificate, your production certificate, or both. At least one is required.
|
||||
+ For each certificate, select the `.p12 file`, and provide the password, if any. Make sure the `bundle ID` for this certificate matches the `bundle ID` of your app. Select `Save`.
|
||||
|
||||
## Contributing to FirebaseUI
|
||||
|
||||
### Contributor License Agreements
|
||||
|
||||
We'd love to accept your sample apps and patches! Before we can take them, we
|
||||
have to jump a couple of legal hurdles.
|
||||
|
||||
Please fill out either the individual or corporate Contributor License Agreement
|
||||
(CLA).
|
||||
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then you'll need to sign an [individual CLA]
|
||||
(https://developers.google.com/open-source/cla/individual).
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a [corporate CLA]
|
||||
(https://developers.google.com/open-source/cla/corporate).
|
||||
|
||||
Follow either of the two links above to access the appropriate CLA and
|
||||
instructions for how to sign and return it. Once we receive it, we'll be able to
|
||||
accept your pull requests.
|
||||
|
||||
### Contribution Process
|
||||
|
||||
1. Submit an issue describing your proposed change to the repo in question.
|
||||
1. The repo owner will respond to your issue promptly.
|
||||
1. If your proposed change is accepted, and you haven't already done so, sign a
|
||||
Contributor License Agreement (see details above).
|
||||
1. Fork the desired repo, develop and test your code changes.
|
||||
1. Ensure that your code adheres to the existing style of the library to which
|
||||
you are contributing.
|
||||
1. Ensure that your code has an appropriate set of unit tests which all pass.
|
||||
1. Submit a pull request
|
||||
Reference in New Issue
Block a user