diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Models/AuthMenu.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Models/AuthMenu.swift index 6c6dfd23a86..cd7cb7a0e59 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Models/AuthMenu.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Models/AuthMenu.swift @@ -40,6 +40,14 @@ enum AuthMenu: String { case removeLastIdTokenChangeListener case verifyClient case deleteApp + case actionType + case continueURL + case requestVerifyEmail + case requestPasswordReset + case resetPassword + case checkActionCode + case applyActionCode + case verifyPasswordResetCode // More intuitively named getter for `rawValue`. var id: String { rawValue } @@ -95,6 +103,23 @@ enum AuthMenu: String { return "Verify Client" case .deleteApp: return "Delete App" + // OOB + case .actionType: + return "Action Type" + case .continueURL: + return "Continue URL" + case .requestVerifyEmail: + return "Request Verify Email" + case .requestPasswordReset: + return "Request Password Reset" + case .resetPassword: + return "Reset Password" + case .checkActionCode: + return "Check Action Code" + case .applyActionCode: + return "Apply Action Code" + case .verifyPasswordResetCode: + return "Verify Password Reset Code" } } @@ -150,6 +175,52 @@ enum AuthMenu: String { self = .verifyClient case "Delete App": self = .deleteApp + case "Action Type": + self = .actionType + case "Continue URL": + self = .continueURL + case "Request Verify Email": + self = .requestVerifyEmail + case "Request Password Reset": + self = .requestPasswordReset + case "Reset Password": + self = .resetPassword + case "Check Action Code": + self = .checkActionCode + case "Apply Action Code": + self = .applyActionCode + case "Verify Password Reset Code": + self = .verifyPasswordResetCode + default: + return nil + } + } +} + +enum ActionCodeRequestType: String { + case email + case `continue` + case inApp + + var name: String { + switch self { + case .email: + return "Email Only" + case .inApp: + return "In-App + Continue URL" + case .continue: + return "Continue URL" + } + } + + init?(rawValue: String) { + switch rawValue { + case "Email Only": + self = .email + case "In-App + Continue URL": + self = .inApp + case "Continue URL": + self = .continue default: return nil } @@ -158,14 +229,14 @@ enum AuthMenu: String { // MARK: DataSourceProvidable -extension AuthMenu: DataSourceProvidable { +class AuthMenuData: DataSourceProvidable { private static var providers: [AuthMenu] { [.google, .apple, .twitter, .microsoft, .gitHub, .yahoo, .facebook, .gameCenter] } static var settingsSection: Section { let header = "Auth Settings" - let item = Item(title: settings.name, hasNestedContent: true) + let item = Item(title: AuthMenu.settings.name, hasNestedContent: true) return Section(headerDescription: header, items: [item]) } @@ -179,7 +250,7 @@ extension AuthMenu: DataSourceProvidable { static var emailPasswordSection: Section { let image = UIImage(named: "firebaseIcon") let header = "Email and Password Login" - let item = Item(title: emailPassword.name, hasNestedContent: true, image: image) + let item = Item(title: AuthMenu.emailPassword.name, hasNestedContent: true, image: image) return Section(headerDescription: header, items: [item]) } @@ -190,10 +261,10 @@ extension AuthMenu: DataSourceProvidable { let shieldSymbol = UIImage.systemImage("lock.shield.fill", tintColor: .systemOrange) let otherOptions = [ - Item(title: passwordless.name, image: lockSymbol), - Item(title: phoneNumber.name, image: phoneSymbol), - Item(title: anonymous.name, image: anonSymbol), - Item(title: custom.name, image: shieldSymbol), + Item(title: AuthMenu.passwordless.name, image: lockSymbol), + Item(title: AuthMenu.phoneNumber.name, image: phoneSymbol), + Item(title: AuthMenu.anonymous.name, image: anonSymbol), + Item(title: AuthMenu.custom.name, image: shieldSymbol), ] let header = "Other Authentication Methods" return Section(headerDescription: header, items: otherOptions) @@ -202,44 +273,58 @@ extension AuthMenu: DataSourceProvidable { static var recaptchaSection: Section { let image = UIImage(named: "firebaseIcon") let header = "Initialize reCAPTCHA Enterprise" - let item = Item(title: initRecaptcha.name, hasNestedContent: false, image: image) + let item = Item(title: AuthMenu.initRecaptcha.name, hasNestedContent: false, image: image) return Section(headerDescription: header, items: [item]) } static var customAuthDomainSection: Section { let image = UIImage(named: "firebaseIcon") let header = "Custom Auth Domain" - let item = Item(title: customAuthDomain.name, hasNestedContent: false, image: image) + let item = Item(title: AuthMenu.customAuthDomain.name, hasNestedContent: false, image: image) return Section(headerDescription: header, items: [item]) } static var appSection: Section { let header = "APP" let items: [Item] = [ - Item(title: getToken.name), - Item(title: getTokenForceRefresh.name), - Item(title: addAuthStateChangeListener.name), - Item(title: removeLastAuthStateChangeListener.name), - Item(title: addIdTokenChangeListener.name), - Item(title: removeLastIdTokenChangeListener.name), - Item(title: verifyClient.name), - Item(title: deleteApp.name), + Item(title: AuthMenu.getToken.name), + Item(title: AuthMenu.getTokenForceRefresh.name), + Item(title: AuthMenu.addAuthStateChangeListener.name), + Item(title: AuthMenu.removeLastAuthStateChangeListener.name), + Item(title: AuthMenu.addIdTokenChangeListener.name), + Item(title: AuthMenu.removeLastIdTokenChangeListener.name), + Item(title: AuthMenu.verifyClient.name), + Item(title: AuthMenu.deleteApp.name), ] return Section(headerDescription: header, items: items) } - static var sections: [Section] { - [settingsSection, providerSection, emailPasswordSection, otherSection, recaptchaSection, - customAuthDomainSection, appSection] + static var oobSection: Section { + let header = "OOB" + let items: [Item] = [ + Item(title: AuthMenu.actionType.name, detailTitle: ActionCodeRequestType.inApp.name), + Item(title: AuthMenu.continueURL.name, detailTitle: "--", isEditable: true), + Item(title: AuthMenu.requestVerifyEmail.name), + Item(title: AuthMenu.requestPasswordReset.name), + Item(title: AuthMenu.resetPassword.name), + Item(title: AuthMenu.checkActionCode.name), + Item(title: AuthMenu.applyActionCode.name), + Item(title: AuthMenu.verifyPasswordResetCode.name), + ] + return Section(headerDescription: header, items: items) } + static var sections: [Section] = + [settingsSection, providerSection, emailPasswordSection, otherSection, recaptchaSection, + customAuthDomainSection, appSection, oobSection] + static var authLinkSections: [Section] { - let allItems = AuthMenu.sections.flatMap { $0.items } + let allItems = AuthMenuData.sections.flatMap { $0.items } let header = "Manage linking between providers" let footer = "Select an unchecked row to link the currently signed in user to that auth provider. To unlink the user from a linked provider, select its corresponding row marked with a checkmark." return [Section(headerDescription: header, footerDescription: footer, items: allItems)] } - var sections: [Section] { AuthMenu.sections } + var sections: [Section] = AuthMenuData.sections } diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Utility/DataSourceProvider/DataSourceProvider.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Utility/DataSourceProvider/DataSourceProvider.swift index 857f58dbee6..12b0b1e9084 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Utility/DataSourceProvider/DataSourceProvider.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Utility/DataSourceProvider/DataSourceProvider.swift @@ -43,6 +43,10 @@ class DataSourceProvider: NSObject, UITableVie return sectionItem(at: indexPath) } + public func updateItem(at indexPath: IndexPath, item: Item) -> DataSource.Section.Item { + return editSectionItem(at: indexPath, item: item) + } + // MARK: - UITableViewDataSource func numberOfSections(in tableView: UITableView) -> Int { @@ -101,6 +105,11 @@ class DataSourceProvider: NSObject, UITableVie return sections[indexPath.section].items[indexPath.row] } + private func editSectionItem(at indexPath: IndexPath, item: Item) -> DataSource.Section.Item { + sections[indexPath.section].items[indexPath.row] = item as! DataSource.Section.Item + return sectionItem(at: indexPath) + } + private func config(_ label: inout UILabel, for section: DataSource.Section) { label.text = section.headerDescription label.textColor = .label diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AccountLinkingViewController.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AccountLinkingViewController.swift index 410c7adc624..eed3c2e25ac 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AccountLinkingViewController.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AccountLinkingViewController.swift @@ -28,7 +28,7 @@ import AuthenticationServices import CryptoKit class AccountLinkingViewController: UIViewController, DataSourceProviderDelegate { - var dataSourceProvider: DataSourceProvider! + var dataSourceProvider: DataSourceProvider! var tableView: UITableView { view as! UITableView } @@ -466,7 +466,7 @@ extension AccountLinkingViewController: DataSourceProvidable { var sections: [Section] { buildSections() } private func buildSections() -> [Section] { - var section = AuthMenu.authLinkSections.first! + var section = AuthMenuData.authLinkSections.first! section.items = section.items.compactMap { item -> Item? in var item = item item.hasNestedContent = false diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift index 7f7350afd1f..88b9578ced3 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift @@ -1,3 +1,5 @@ +@testable import FirebaseAuth + // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -11,13 +13,13 @@ // 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. +// [START auth_import] +import FirebaseCore // For Sign in with Facebook import FBSDKLoginKit -@testable import FirebaseAuth -// [START auth_import] -import FirebaseCore +// For Sign in with Game Center import GameKit // For Sign in with Google @@ -30,11 +32,17 @@ import AuthenticationServices import CryptoKit private let kFacebookAppID = "ENTER APP ID HERE" +private let kContinueUrl = "Enter URL" class AuthViewController: UIViewController, DataSourceProviderDelegate { - var dataSourceProvider: DataSourceProvider! + var dataSourceProvider: DataSourceProvider! var authStateDidChangeListeners: [AuthStateDidChangeListenerHandle] = [] var IDTokenDidChangeListeners: [IDTokenDidChangeListenerHandle] = [] + var actionCodeContinueURL: URL? + var actionCodeRequestType: ActionCodeRequestType = .inApp + + let spinner = UIActivityIndicatorView(style: .medium) + var tableView: UITableView { view as! UITableView } override func loadView() { view = UITableView(frame: .zero, style: .insetGrouped) @@ -46,6 +54,24 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { configureDataSourceProvider() } + private func showSpinner() { + spinner.center = view.center + spinner.startAnimating() + view.addSubview(spinner) + } + + private func hideSpinner() { + spinner.stopAnimating() + spinner.removeFromSuperview() + } + + private func actionCodeSettings() -> ActionCodeSettings { + let settings = ActionCodeSettings() + settings.url = actionCodeContinueURL + settings.handleCodeInApp = (actionCodeRequestType == .inApp) + return settings + } + // MARK: - DataSourceProviderDelegate func didSelectRowAt(_ indexPath: IndexPath, on tableView: UITableView) { @@ -121,6 +147,30 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { case .deleteApp: deleteApp() + + case .actionType: + toggleActionCodeRequestType(at: indexPath) + + case .continueURL: + changeActionCodeContinueURL(at: indexPath) + + case .requestVerifyEmail: + requestVerifyEmail() + + case .requestPasswordReset: + requestPasswordReset() + + case .resetPassword: + resetPassword() + + case .checkActionCode: + checkActionCode() + + case .applyActionCode: + applyActionCode() + + case .verifyPasswordResetCode: + verifyPasswordResetCode() } } @@ -330,16 +380,9 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { } private func performCustomAuthDomainFlow() { - let prompt = UIAlertController(title: nil, message: "Enter Custom Auth Domain For Auth:", - preferredStyle: .alert) - prompt.addTextField() - let okAction = UIAlertAction(title: "OK", style: .default) { action in - let domain = prompt.textFields?[0].text ?? "" - AppManager.shared.auth().customAuthDomain = domain - print("Successfully set auth domain to: \(domain)") - } - prompt.addAction(okAction) - present(prompt, animated: true) + showTextInputPrompt(with: "Enter Custom Auth Domain For Auth: ", completion: { newDomain in + AppManager.shared.auth().customAuthDomain = newDomain + }) } private func getUserTokenResult(force: Bool) { @@ -400,7 +443,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { IDTokenDidChangeListeners.append(handle) } - func removeIDTokenListener() { + private func removeIDTokenListener() { guard !IDTokenDidChangeListeners.isEmpty else { print("No remaining ID Token Did Change Listeners.") return @@ -412,7 +455,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { print("ID Token Did Change Listener #\(index) was removed.") } - func verifyClient() { + private func verifyClient() { AppManager.shared.auth().tokenManager.getTokenInternal { token, error in if token == nil { print("Verify iOS Client failed.") @@ -468,7 +511,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { } } - func deleteApp() { + private func deleteApp() { AppManager.shared.app.delete { success in if success { print("App deleted successfully.") @@ -478,11 +521,322 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { } } + private func toggleActionCodeRequestType(at indexPath: IndexPath) { + switch actionCodeRequestType { + case .inApp: + actionCodeRequestType = .continue + case .continue: + actionCodeRequestType = .email + case .email: + actionCodeRequestType = .inApp + } + dataSourceProvider.updateItem( + at: indexPath, + item: Item(title: AuthMenu.actionType.name, detailTitle: actionCodeRequestType.name) + ) + tableView.reloadData() + } + + private func changeActionCodeContinueURL(at indexPath: IndexPath) { + showTextInputPrompt(with: "Continue URL:", completion: { newContinueURL in + self.actionCodeContinueURL = URL(string: newContinueURL) + print("Successfully set Continue URL to: \(newContinueURL)") + self.dataSourceProvider.updateItem( + at: indexPath, + item: Item( + title: AuthMenu.continueURL.name, + detailTitle: self.actionCodeContinueURL?.absoluteString, + isEditable: true + ) + ) + self.tableView.reloadData() + }) + } + + private func requestVerifyEmail() { + showSpinner() + let completionHandler: (Error?) -> Void = { [weak self] error in + guard let self = self else { return } + self.hideSpinner() + + if let error = error { + let errorMessage = "Error sending verification email: \(error.localizedDescription)" + showAlert(for: errorMessage) + print(errorMessage) + } else { + let successMessage = "Verification email sent successfully!" + showAlert(for: successMessage) + print(successMessage) + } + } + if actionCodeRequestType == .email { + AppManager.shared.auth().currentUser?.sendEmailVerification(completion: completionHandler) + } else { + if actionCodeContinueURL == nil { + print("Error: Action code continue URL is nil.") + return + } + AppManager.shared.auth().currentUser?.sendEmailVerification( + with: actionCodeSettings(), + completion: completionHandler + ) + } + } + + func requestPasswordReset() { + showTextInputPrompt(with: "Email:", completion: { email in + print("Sending password reset link to: \(email)") + self.showSpinner() + let completionHandler: (Error?) -> Void = { [weak self] error in + guard let self = self else { return } + self.hideSpinner() + if let error = error { + print("Request password reset failed: \(error)") + showAlert(for: error.localizedDescription) + return + } + print("Request password reset succeeded.") + showAlert(for: "Sent!") + } + if self.actionCodeRequestType == .email { + AppManager.shared.auth().sendPasswordReset(withEmail: email, completion: completionHandler) + } else { + guard let actionCodeContinueURL = self.actionCodeContinueURL else { + print("Error: Action code continue URL is nil.") + return + } + AppManager.shared.auth().sendPasswordReset( + withEmail: email, + actionCodeSettings: self.actionCodeSettings(), + completion: completionHandler + ) + } + }) + } + + private func resetPassword() { + showSpinner() + let completionHandler: (Error?) -> Void = { [weak self] error in + guard let self = self else { return } + self.hideSpinner() + if let error = error { + print("Password reset failed \(error)") + showAlert(for: error.localizedDescription) + return + } + print("Password reset succeeded") + showAlert(for: "Password reset succeeded!") + } + showTextInputPrompt(with: "OOB Code:") { + code in + self.showTextInputPrompt(with: "New Password") { + password in + AppManager.shared.auth().confirmPasswordReset( + withCode: code, + newPassword: password, + completion: completionHandler + ) + } + } + } + + private func nameForActionCodeOperation(_ operation: ActionCodeOperation) -> String { + switch operation { + case .verifyEmail: + return "Verify Email" + case .recoverEmail: + return "Recover Email" + case .passwordReset: + return "Password Reset" + case .emailLink: + return "Email Sign-In Link" + case .verifyAndChangeEmail: + return "Verify Before Change Email" + case .revertSecondFactorAddition: + return "Revert Second Factor Addition" + case .unknown: + return "Unknown action" + } + } + + private func checkActionCode() { + showSpinner() + let completionHandler: (ActionCodeInfo?, Error?) -> Void = { [weak self] info, error in + guard let self = self else { return } + self.hideSpinner() + if let error = error { + print("Check action code failed: \(error)") + showAlert(for: error.localizedDescription) + return + } + guard let info = info else { return } + print("Check action code succeeded") + let email = info.email + let previousEmail = info.previousEmail + let message = previousEmail != nil ? "\(previousEmail!) -> \(email)" : email + let operation = self.nameForActionCodeOperation(info.operation) + showAlert(for: operation) + } + showTextInputPrompt(with: "OOB Code:") { + oobCode in + AppManager.shared.auth().checkActionCode(oobCode, completion: completionHandler) + } + } + + private func applyActionCode() { + showSpinner() + let completionHandler: (Error?) -> Void = { [weak self] error in + guard let self = self else { return } + self.hideSpinner() + if let error = error { + print("Apply action code failed \(error)") + showAlert(for: error.localizedDescription) + return + } + print("Apply action code succeeded") + showAlert(for: "Action code was properly applied") + } + showTextInputPrompt(with: "OOB Code: ") { + oobCode in + AppManager.shared.auth().applyActionCode(oobCode, completion: completionHandler) + } + } + + private func verifyPasswordResetCode() { + showSpinner() + let completionHandler: (String?, Error?) -> Void = { [weak self] email, error in + guard let self = self else { return } + self.hideSpinner() + if let error = error { + print("Verify password reset code failed \(error)") + showAlert(for: error.localizedDescription) + return + } + print("Verify password resest code succeeded.") + showAlert(for: "Code verified for email: \(email)") + } + showTextInputPrompt(with: "OOB Code: ") { + oobCode in + AppManager.shared.auth().verifyPasswordResetCode(oobCode, completion: completionHandler) + } + } + // MARK: - Private Helpers + private func showTextInputPrompt(with message: String, completion: ((String) -> Void)? = nil) { + let editController = UIAlertController( + title: message, + message: nil, + preferredStyle: .alert + ) + editController.addTextField() + + let saveHandler: (UIAlertAction) -> Void = { _ in + let text = editController.textFields?.first?.text ?? "" + if let completion { + completion(text) + } + } + + let cancelHandler: (UIAlertAction) -> Void = { _ in + if let completion { + completion("") + } + } + + editController.addAction(UIAlertAction(title: "Save", style: .default, handler: saveHandler)) + editController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: cancelHandler)) + + // Assuming `self` is a view controller + present(editController, animated: true, completion: nil) + } + + private func showQRCodePromptWithTextInput(with message: String, url: String, + completion: ((String) -> Void)? = nil) { + // Create a UIAlertController + let alertController = UIAlertController( + title: "QR Code Prompt", + message: message, + preferredStyle: .alert + ) + + // Add a text field for input + alertController.addTextField { textField in + textField.placeholder = "Enter text" + } + + // Create a UIImage from the URL + guard let image = generateQRCode(from: url) else { + print("Failed to generate QR code") + return + } + + // Create an image view to display the QR code + let imageView = UIImageView(image: image) + imageView.contentMode = .scaleAspectFit + imageView.translatesAutoresizingMaskIntoConstraints = false + + // Add the image view to the alert controller + alertController.view.addSubview(imageView) + + // Add constraints to position the image view + NSLayoutConstraint.activate([ + imageView.topAnchor.constraint(equalTo: alertController.view.topAnchor, constant: 20), + imageView.centerXAnchor.constraint(equalTo: alertController.view.centerXAnchor), + imageView.widthAnchor.constraint(equalToConstant: 200), + imageView.heightAnchor.constraint(equalToConstant: 200), + ]) + + // Add actions + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) + let submitAction = UIAlertAction(title: "Submit", style: .default) { _ in + if let completion, + let text = alertController.textFields?.first?.text { + completion(text) + } + } + + alertController.addAction(cancelAction) + alertController.addAction(submitAction) + + // Present the alert controller + UIApplication.shared.windows.first?.rootViewController?.present( + alertController, + animated: true, + completion: nil + ) + } + + // Function to generate QR code from a string + private func generateQRCode(from string: String) -> UIImage? { + let data = string.data(using: String.Encoding.ascii) + + if let filter = CIFilter(name: "CIQRCodeGenerator") { + filter.setValue(data, forKey: "inputMessage") + let transform = CGAffineTransform(scaleX: 10, y: 10) + + if let output = filter.outputImage?.transformed(by: transform) { + return UIImage(ciImage: output) + } + } + + return nil + } + + func showAlert(for message: String) { + let alertController = UIAlertController( + title: message, + message: nil, + preferredStyle: .alert + ) + alertController.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default)) + } + private func configureDataSourceProvider() { - let tableView = view as! UITableView - dataSourceProvider = DataSourceProvider(dataSource: AuthMenu.sections, tableView: tableView) + dataSourceProvider = DataSourceProvider( + dataSource: AuthMenuData.sections, + tableView: tableView + ) dataSourceProvider.delegate = self } diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/SettingsViewController.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/SettingsViewController.swift index 3ff02c0a481..6c673022e29 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/SettingsViewController.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/SettingsViewController.swift @@ -197,24 +197,6 @@ class SettingsViewController: UIViewController, DataSourceProviderDelegate { options: .transitionCrossDissolve, animations: { tableView.reloadData() }) } - - func showPromptWithTitle(_ title: String, message: String, showCancelButton: Bool, - completion: @escaping (Bool, String?) -> Void) { - let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) - - alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in - let userInput = alertController.textFields?.first?.text - completion(true, userInput) - })) - - if showCancelButton { - alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in - completion(false, nil) - })) - } - - alertController.addTextField(configurationHandler: nil) - } } // MARK: - Extending a `AuthSettings` to conform to `DataSourceProvidable` @@ -272,15 +254,6 @@ extension AuthSettings: DataSourceProvidable { func appCredentialString() -> String { if let credential = AppManager.shared.auth().appCredentialManager.credential { - let message = "receipt: \(credential.receipt)\nsecret: \(credential.secret)" - - showPromptWithTitle("Clear App Credential?", message: message, - showCancelButton: true) { userPressedOK, _ in - if userPressedOK { - AppManager.shared.auth().appCredentialManager.clearCredential() - } - } - } let truncatedReceipt = truncatedString(string: credential.receipt, length: 13) let truncatedSecret = truncatedString(string: credential.secret ?? "", length: 13) return "\(truncatedReceipt)/\(truncatedSecret)"