mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-10 22:45:59 +05:45
701 lines
22 KiB
Swift
701 lines
22 KiB
Swift
/*
|
||
* Copyright 2017 Google
|
||
*
|
||
* 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
|
||
|
||
import FirebaseCommunity.FirebaseAuth
|
||
import GoogleSignIn
|
||
|
||
final class ViewController: UIViewController, UITextFieldDelegate, AuthUIDelegate {
|
||
/// The profile image for the currently signed-in user.
|
||
@IBOutlet weak var profileImage: UIImageView!
|
||
|
||
/// The display name for the currently signed-in user.
|
||
@IBOutlet weak var displayNameLabel: UILabel!
|
||
|
||
/// The email for the currently signed-in user.
|
||
@IBOutlet weak var emailLabel: UILabel!
|
||
|
||
/// The ID for the currently signed-in user.
|
||
@IBOutlet weak var userIDLabel: UILabel!
|
||
|
||
/// The list of providers for the currently signed-in user.
|
||
@IBOutlet weak var providerListLabel: UILabel!
|
||
|
||
/// The picker for the list of action types.
|
||
@IBOutlet weak var actionTypePicker: UIPickerView!
|
||
|
||
/// The picker for the list of actions.
|
||
@IBOutlet weak var actionPicker: UIPickerView!
|
||
|
||
/// The picker for the list of credential types.
|
||
@IBOutlet weak var credentialTypePicker: UIPickerView!
|
||
|
||
/// The label for the "email" text field.
|
||
@IBOutlet weak var emailInputLabel: UILabel!
|
||
|
||
/// The "email" text field.
|
||
@IBOutlet weak var emailField: UITextField!
|
||
|
||
/// The label for the "password" text field.
|
||
@IBOutlet weak var passwordInputLabel: UILabel!
|
||
|
||
/// The "password" text field.
|
||
@IBOutlet weak var passwordField: UITextField!
|
||
|
||
/// The "phone" text field.
|
||
@IBOutlet weak var phoneField: UITextField!
|
||
|
||
/// The scroll view holding all content.
|
||
@IBOutlet weak var scrollView: UIScrollView!
|
||
|
||
// The active keyboard input field.
|
||
var activeField: UITextField?
|
||
|
||
/// The currently selected action type.
|
||
fileprivate var actionType = ActionType(rawValue: 0)! {
|
||
didSet {
|
||
if actionType != oldValue {
|
||
actionPicker.reloadAllComponents()
|
||
actionPicker.selectRow(actionType == .auth ? authAction.rawValue : userAction.rawValue,
|
||
inComponent: 0, animated: false)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// The currently selected auth action.
|
||
fileprivate var authAction = AuthAction(rawValue: 0)!
|
||
|
||
/// The currently selected user action.
|
||
fileprivate var userAction = UserAction(rawValue: 0)!
|
||
|
||
/// The currently selected credential.
|
||
fileprivate var credentialType = CredentialType(rawValue: 0)!
|
||
|
||
/// The current Firebase user.
|
||
fileprivate var user: User? = nil {
|
||
didSet {
|
||
if user?.uid != oldValue?.uid {
|
||
actionTypePicker.reloadAllComponents()
|
||
actionType = ActionType(rawValue: actionTypePicker.selectedRow(inComponent: 0))!
|
||
}
|
||
}
|
||
}
|
||
|
||
func registerForKeyboardNotifications() {
|
||
NotificationCenter.default.addObserver(self,
|
||
selector:
|
||
#selector(keyboardWillBeShown(notification:)),
|
||
name: NSNotification.Name.UIKeyboardWillShow,
|
||
object: nil)
|
||
NotificationCenter.default.addObserver(self,
|
||
selector: #selector(keyboardWillBeHidden(notification:)),
|
||
name: NSNotification.Name.UIKeyboardWillHide,
|
||
object: nil)
|
||
}
|
||
|
||
func deregisterFromKeyboardNotifications() {
|
||
NotificationCenter.default.removeObserver(self,
|
||
name: NSNotification.Name.UIKeyboardWillShow,
|
||
object: nil)
|
||
NotificationCenter.default.removeObserver(self,
|
||
name: NSNotification.Name.UIKeyboardWillHide,
|
||
object: nil)
|
||
}
|
||
|
||
func keyboardWillBeShown(notification: NSNotification) {
|
||
scrollView.isScrollEnabled = true
|
||
let info = notification.userInfo!
|
||
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
|
||
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)
|
||
|
||
scrollView.contentInset = contentInsets
|
||
scrollView.scrollIndicatorInsets = contentInsets
|
||
|
||
var aRect = self.view.frame
|
||
aRect.size.height -= keyboardSize!.height
|
||
if let activeField = activeField {
|
||
if (!aRect.contains(activeField.frame.origin)) {
|
||
scrollView.scrollRectToVisible(activeField.frame, animated: true)
|
||
}
|
||
}
|
||
}
|
||
|
||
func keyboardWillBeHidden(notification: NSNotification){
|
||
let info = notification.userInfo!
|
||
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
|
||
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
|
||
scrollView.contentInset = contentInsets
|
||
scrollView.scrollIndicatorInsets = contentInsets
|
||
self.view.endEditing(true)
|
||
scrollView.isScrollEnabled = false
|
||
}
|
||
|
||
func textFieldDidBeginEditing(_ textField: UITextField) {
|
||
activeField = textField
|
||
}
|
||
|
||
func textFieldDidEndEditing(_ textField: UITextField) {
|
||
activeField = nil
|
||
}
|
||
|
||
func dismissKeyboard() {
|
||
view.endEditing(true)
|
||
}
|
||
|
||
func verify(phoneNumber: String, completion: @escaping (PhoneAuthCredential?, Error?) -> Void) {
|
||
if #available(iOS 8.0, *) {
|
||
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate:self) {
|
||
verificationID, error in
|
||
guard error == nil else {
|
||
completion(nil, error)
|
||
return
|
||
}
|
||
let codeAlertController =
|
||
UIAlertController(title: "Enter Code", message: nil, preferredStyle: .alert)
|
||
codeAlertController.addTextField { textfield in
|
||
textfield.placeholder = "SMS Code"
|
||
textfield.keyboardType = UIKeyboardType.numberPad
|
||
}
|
||
codeAlertController.addAction(UIAlertAction(title: "OK",
|
||
style: .default,
|
||
handler: { (UIAlertAction) in
|
||
let code = codeAlertController.textFields!.first!.text!
|
||
let phoneCredential =
|
||
PhoneAuthProvider.provider().credential(withVerificationID: verificationID ?? "",
|
||
verificationCode: code)
|
||
completion(phoneCredential, nil)
|
||
}))
|
||
self.present(codeAlertController, animated: true, completion: nil)
|
||
}
|
||
}
|
||
}
|
||
/// The user's photo URL used by the last network request for its contents.
|
||
fileprivate var lastPhotoURL: URL? = nil
|
||
|
||
override func viewDidLoad() {
|
||
GIDSignIn.sharedInstance().uiDelegate = self
|
||
updateUserInfo(Auth.auth())
|
||
NotificationCenter.default.addObserver(forName: .AuthStateDidChange,
|
||
object: Auth.auth(), queue: nil) { notification in
|
||
self.updateUserInfo(notification.object as? Auth)
|
||
}
|
||
phoneField.delegate = self
|
||
registerForKeyboardNotifications()
|
||
|
||
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
|
||
scrollView.addGestureRecognizer(tap)
|
||
}
|
||
|
||
override func viewWillDisappear(_ animated: Bool) {
|
||
deregisterFromKeyboardNotifications()
|
||
}
|
||
|
||
/// Executes the action designated by the operator on the UI.
|
||
@IBAction func execute(_ sender: UIButton) {
|
||
switch actionType {
|
||
case .auth:
|
||
switch authAction {
|
||
case .fetchProviderForEmail:
|
||
Auth.auth().fetchProviders(forEmail: emailField.text!) { providers, error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Providers", message: providers?.joined(separator: ", "))
|
||
}
|
||
}
|
||
case .signInAnonymously:
|
||
Auth.auth().signInAnonymously() { user, error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Signed In Anonymously")
|
||
}
|
||
}
|
||
case .signInWithCredential:
|
||
getCredential() { credential in
|
||
Auth.auth().signIn(with: credential) { user, error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Signed In With Credential", message: user?.textDescription)
|
||
}
|
||
}
|
||
}
|
||
case .createUser:
|
||
Auth.auth().createUser(withEmail: emailField.text!, password: passwordField.text!) {
|
||
user, error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Signed In With Credential", message: user?.textDescription)
|
||
}
|
||
}
|
||
case .signOut:
|
||
try! Auth.auth().signOut()
|
||
GIDSignIn.sharedInstance().signOut()
|
||
}
|
||
case .user:
|
||
switch userAction {
|
||
case .updateEmail:
|
||
user!.updateEmail(to: emailField.text!) { error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Updated Email", message: self.user?.email)
|
||
}
|
||
}
|
||
case .updatePhone:
|
||
let phoneNumber = phoneField.text
|
||
self.verify(phoneNumber: phoneNumber!, completion: { (phoneAuthCredential, error) in
|
||
guard error == nil else {
|
||
self.showAlert(title: "Error", message: error!.localizedDescription)
|
||
return
|
||
}
|
||
self.user!.updatePhoneNumber(phoneAuthCredential!, completion: { error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Updated Phone Number")
|
||
self.updateUserInfo(Auth.auth())
|
||
}
|
||
})
|
||
})
|
||
case .updatePassword:
|
||
user!.updatePassword(to: passwordField.text!) { error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Updated Password")
|
||
}
|
||
}
|
||
case .reload:
|
||
user!.reload() { error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Reloaded", message: self.user?.textDescription)
|
||
}
|
||
}
|
||
case .reauthenticate:
|
||
getCredential() { credential in
|
||
self.user!.reauthenticate(with: credential) { error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Reauthenticated", message: self.user?.textDescription)
|
||
}
|
||
}
|
||
}
|
||
case .getToken:
|
||
user!.getIDToken() { token, error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Got ID Token", message: token)
|
||
}
|
||
}
|
||
case .linkWithCredential:
|
||
getCredential() { credential in
|
||
self.user!.link(with: credential) { user, error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Linked With Credential", message: user?.textDescription)
|
||
}
|
||
}
|
||
}
|
||
case .deleteAccount:
|
||
user!.delete() { error in
|
||
self.ifNoError(error) {
|
||
self.showAlert(title: "Deleted Account")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Gets an AuthCredential potentially asynchronously.
|
||
private func getCredential(completion: @escaping (AuthCredential) -> Void) {
|
||
switch credentialType {
|
||
case .google:
|
||
GIDSignIn.sharedInstance().delegate = GoogleSignInDelegate(completion: { user, error in
|
||
self.ifNoError(error) {
|
||
completion(GoogleAuthProvider.credential(
|
||
withIDToken: user!.authentication.idToken,
|
||
accessToken: user!.authentication.accessToken))
|
||
}
|
||
})
|
||
GIDSignIn.sharedInstance().signIn()
|
||
case .password:
|
||
completion(EmailAuthProvider.credential(withEmail: emailField.text!,
|
||
password: passwordField.text!))
|
||
case .phone:
|
||
let phoneNumber = phoneField.text
|
||
self.verify(phoneNumber: phoneNumber!, completion: { (phoneAuthCredential, error) in
|
||
guard error == nil else {
|
||
self.showAlert(title: "Error", message: error!.localizedDescription)
|
||
return
|
||
}
|
||
completion(phoneAuthCredential!)
|
||
})
|
||
}
|
||
}
|
||
|
||
/// Updates user's profile image and info text.
|
||
private func updateUserInfo(_ auth: Auth?) {
|
||
user = auth?.currentUser
|
||
displayNameLabel.text = user?.displayName
|
||
emailLabel.text = user?.email
|
||
userIDLabel.text = user?.uid
|
||
let providers = user?.providerData.map { userInfo in userInfo.providerID }
|
||
providerListLabel.text = providers?.joined(separator: ", ")
|
||
if let photoURL = user?.photoURL {
|
||
lastPhotoURL = photoURL
|
||
let queue: DispatchQueue
|
||
if #available(iOS 8.0, *) {
|
||
queue = DispatchQueue.global(qos: .background)
|
||
} else {
|
||
queue = DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.background)
|
||
}
|
||
queue.async {
|
||
if let imageData = try? Data(contentsOf: photoURL) {
|
||
let image = UIImage(data: imageData)
|
||
DispatchQueue.main.async {
|
||
if self.lastPhotoURL == photoURL {
|
||
self.profileImage.image = image
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
lastPhotoURL = nil
|
||
self.profileImage.image = nil
|
||
}
|
||
updateControls()
|
||
}
|
||
|
||
// Updates the states of the UI controls.
|
||
fileprivate func updateControls() {
|
||
let action: Action
|
||
switch actionType {
|
||
case .auth:
|
||
action = authAction
|
||
case .user:
|
||
action = userAction
|
||
}
|
||
let isCredentialEnabled = action.requiresCredential
|
||
credentialTypePicker.isUserInteractionEnabled = isCredentialEnabled
|
||
credentialTypePicker.alpha = isCredentialEnabled ? 1.0 : 0.6
|
||
let isEmailEnabled = isCredentialEnabled && credentialType.requiresEmail || action.requiresEmail
|
||
emailInputLabel.alpha = isEmailEnabled ? 1.0 : 0.6
|
||
emailField.isEnabled = isEmailEnabled
|
||
let isPasswordEnabled = isCredentialEnabled && credentialType.requiresPassword ||
|
||
action.requiresPassword
|
||
passwordInputLabel.alpha = isPasswordEnabled ? 1.0 : 0.6
|
||
passwordField.isEnabled = isPasswordEnabled
|
||
phoneField.isEnabled = credentialType.requiresPhone || action.requiresPhoneNumber
|
||
}
|
||
|
||
fileprivate func showAlert(title: String, message: String? = "") {
|
||
if #available(iOS 8.0, *) {
|
||
let alertController =
|
||
UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||
alertController.addAction(UIAlertAction(title: "OK",
|
||
style: .default,
|
||
handler: { (UIAlertAction) in
|
||
alertController.dismiss(animated: true, completion: nil)
|
||
}))
|
||
self.present(alertController, animated: true, completion: nil)
|
||
} else {
|
||
UIAlertView(title: title,
|
||
message: message ?? "(NULL)",
|
||
delegate: nil,
|
||
cancelButtonTitle: nil,
|
||
otherButtonTitles: "OK").show()
|
||
}
|
||
}
|
||
|
||
private func ifNoError(_ error: Error?, execute: () -> Void) {
|
||
guard error == nil else {
|
||
showAlert(title: "Error", message: error!.localizedDescription)
|
||
return
|
||
}
|
||
execute()
|
||
}
|
||
}
|
||
|
||
extension ViewController : GIDSignInUIDelegate {
|
||
func sign(_ signIn: GIDSignIn!, present viewController: UIViewController!) {
|
||
present(viewController, animated: true, completion: nil)
|
||
}
|
||
|
||
func sign(_ signIn: GIDSignIn!, dismiss viewController: UIViewController!) {
|
||
dismiss(animated: true, completion: nil)
|
||
}
|
||
}
|
||
|
||
extension ViewController : UIPickerViewDataSource {
|
||
func numberOfComponents(in pickerView: UIPickerView) -> Int {
|
||
return 1
|
||
}
|
||
|
||
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||
switch pickerView {
|
||
case actionTypePicker:
|
||
if Auth.auth().currentUser != nil {
|
||
return ActionType.countWithUser
|
||
} else {
|
||
return ActionType.countWithoutUser
|
||
}
|
||
case actionPicker:
|
||
switch actionType {
|
||
case .auth:
|
||
return AuthAction.count
|
||
case .user:
|
||
return UserAction.count
|
||
}
|
||
case credentialTypePicker:
|
||
return CredentialType.count
|
||
default:
|
||
return 0
|
||
}
|
||
}
|
||
}
|
||
|
||
extension ViewController : UIPickerViewDelegate {
|
||
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int)
|
||
-> String? {
|
||
switch pickerView {
|
||
case actionTypePicker:
|
||
return ActionType(rawValue: row)!.text
|
||
case actionPicker:
|
||
switch actionType {
|
||
case .auth:
|
||
return AuthAction(rawValue: row)!.text
|
||
case .user:
|
||
return UserAction(rawValue: row)!.text
|
||
}
|
||
case credentialTypePicker:
|
||
return CredentialType(rawValue: row)!.text
|
||
default:
|
||
return nil
|
||
}
|
||
}
|
||
|
||
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
||
switch pickerView {
|
||
case actionTypePicker:
|
||
actionType = ActionType(rawValue: row)!
|
||
case actionPicker:
|
||
switch actionType {
|
||
case .auth:
|
||
authAction = AuthAction(rawValue: row)!
|
||
case .user:
|
||
userAction = UserAction(rawValue: row)!
|
||
}
|
||
case credentialTypePicker:
|
||
credentialType = CredentialType(rawValue: row)!
|
||
default:
|
||
break
|
||
}
|
||
updateControls()
|
||
}
|
||
}
|
||
|
||
/// An adapter class to pass GoogleSignIn delegate method to a block.
|
||
fileprivate final class GoogleSignInDelegate: NSObject, GIDSignInDelegate {
|
||
|
||
private let completion: (GIDGoogleUser?, Error?) -> Void
|
||
private var retainedSelf: GoogleSignInDelegate?
|
||
|
||
init(completion: @escaping (GIDGoogleUser?, Error?) -> Void) {
|
||
self.completion = completion
|
||
super.init()
|
||
retainedSelf = self
|
||
}
|
||
|
||
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser?, withError error: Error?) {
|
||
completion(user, error)
|
||
retainedSelf = nil
|
||
}
|
||
}
|
||
|
||
/// The list of all possible action types.
|
||
fileprivate enum ActionType: Int {
|
||
|
||
case auth, user
|
||
|
||
// Count of action types when no user is signed in.
|
||
static var countWithoutUser: Int {
|
||
return ActionType.auth.rawValue + 1
|
||
}
|
||
|
||
// Count of action types when a user is signed in.
|
||
static var countWithUser: Int {
|
||
return ActionType.user.rawValue + 1
|
||
}
|
||
|
||
/// The text description for a particular enum value.
|
||
var text : String {
|
||
switch self {
|
||
case .auth:
|
||
return "Auth"
|
||
case .user:
|
||
return "User"
|
||
}
|
||
}
|
||
}
|
||
|
||
fileprivate protocol Action {
|
||
/// The text description for the particular action.
|
||
var text: String { get }
|
||
|
||
/// Whether or not the action requires a credential.
|
||
var requiresCredential : Bool { get }
|
||
|
||
/// Whether or not the action requires an email.
|
||
var requiresEmail: Bool { get }
|
||
|
||
/// Whether or not the credential requires a password.
|
||
var requiresPassword: Bool { get }
|
||
|
||
/// Whether or not the credential requires a phone number.
|
||
var requiresPhoneNumber: Bool { get }
|
||
}
|
||
|
||
/// The list of all possible actions the operator can take on the Auth object.
|
||
fileprivate enum AuthAction: Int, Action {
|
||
|
||
case fetchProviderForEmail, signInAnonymously, signInWithCredential, createUser, signOut
|
||
|
||
/// Total number of auth actions.
|
||
static var count: Int {
|
||
return AuthAction.signOut.rawValue + 1
|
||
}
|
||
|
||
var text : String {
|
||
switch self {
|
||
case .fetchProviderForEmail:
|
||
return "Fetch Provider ⬇️"
|
||
case .signInAnonymously:
|
||
return "Sign In Anonymously"
|
||
case .signInWithCredential:
|
||
return "Sign In w/ Credential ↙️"
|
||
case .createUser:
|
||
return "Create User ⬇️"
|
||
case .signOut:
|
||
return "Sign Out"
|
||
}
|
||
}
|
||
|
||
var requiresCredential : Bool {
|
||
return self == .signInWithCredential
|
||
}
|
||
|
||
var requiresEmail : Bool {
|
||
return self == .fetchProviderForEmail || self == .createUser
|
||
}
|
||
|
||
var requiresPassword : Bool {
|
||
return self == .createUser
|
||
}
|
||
|
||
var requiresPhoneNumber: Bool {
|
||
return false
|
||
}
|
||
}
|
||
|
||
/// The list of all possible actions the operator can take on the User object.
|
||
fileprivate enum UserAction: Int, Action {
|
||
|
||
case updateEmail, updatePhone, updatePassword, reload, reauthenticate, getToken,
|
||
linkWithCredential, deleteAccount
|
||
|
||
/// Total number of user actions.
|
||
static var count: Int {
|
||
return UserAction.deleteAccount.rawValue + 1
|
||
}
|
||
|
||
var text : String {
|
||
switch self {
|
||
case .updateEmail:
|
||
return "Update Email ⬇️"
|
||
case .updatePhone:
|
||
if #available(iOS 8.0, *) {
|
||
return "Update Phone ⬇️"
|
||
} else {
|
||
return "-"
|
||
}
|
||
case .updatePassword:
|
||
return "Update Password ⬇️"
|
||
case .reload:
|
||
return "Reload"
|
||
case .reauthenticate:
|
||
return "Reauthenticate ↙️"
|
||
case .getToken:
|
||
return "Get Token"
|
||
case .linkWithCredential:
|
||
return "Link With Credential ↙️"
|
||
case .deleteAccount:
|
||
return "Delete Account"
|
||
}
|
||
}
|
||
|
||
var requiresCredential : Bool {
|
||
return self == .reauthenticate || self == .linkWithCredential
|
||
}
|
||
|
||
var requiresEmail : Bool {
|
||
return self == .updateEmail
|
||
}
|
||
|
||
var requiresPassword : Bool {
|
||
return self == .updatePassword
|
||
}
|
||
|
||
var requiresPhoneNumber : Bool {
|
||
return self == .updatePhone
|
||
}
|
||
|
||
}
|
||
|
||
/// The list of all possible credential types the operator can use to sign in or link.
|
||
fileprivate enum CredentialType: Int {
|
||
|
||
case google, password, phone
|
||
|
||
/// Total number of enum values.
|
||
static var count: Int {
|
||
return CredentialType.phone.rawValue + 1
|
||
}
|
||
|
||
/// The text description for a particular enum value.
|
||
var text : String {
|
||
switch self {
|
||
case .google:
|
||
return "Google"
|
||
case .password:
|
||
return "Password ➡️️"
|
||
case .phone:
|
||
if #available(iOS 8.0, *) {
|
||
return "Phone ➡️️"
|
||
} else {
|
||
return "-"
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Whether or not the credential requires an email.
|
||
var requiresEmail : Bool {
|
||
return self == .password
|
||
}
|
||
|
||
/// Whether or not the credential requires a password.
|
||
var requiresPassword : Bool {
|
||
return self == .password
|
||
}
|
||
|
||
/// Whether or not the credential requires a phone number.
|
||
var requiresPhone : Bool {
|
||
return self == .phone
|
||
}
|
||
}
|
||
|
||
fileprivate extension User {
|
||
var textDescription: String {
|
||
return self.displayName ?? self.email ?? self.uid
|
||
}
|
||
}
|