diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/Localizable.xcstrings b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/Localizable.xcstrings index 746fadc043..66e12e28f6 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/Localizable.xcstrings +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/Localizable.xcstrings @@ -3053,6 +3053,10 @@ } } }, + "Please tap on the link in your email to complete verification." : { + "comment" : "A message displayed in a sheet that appears after a user requests email verification. It instructs the user to tap on the link in their email to complete the verification process.", + "isCommentAutoGenerated" : true + }, "PrivacyPolicy" : { "comment" : "Text linked to a web page with the Privacy Policy content.", "extractionState" : "manual", @@ -4963,5 +4967,5 @@ } }, - "version" : "1.0" + "version" : "1.1" } \ No newline at end of file diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift index f119cf91b8..8b155a94ae 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift @@ -103,6 +103,13 @@ public class StringUtils { return localizedString(for: "EnterYourPassword") } + /// Update password title + /// found in: + /// - UpdatePasswordView + public var updatePasswordTitle: String { + return localizedString(for: "UpdatePasswordTitle") + } + /// Password recovery title /// found in: /// - PasswordRecoveryView diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/MFAManagementView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/MFAManagementView.swift index 0614373a1c..a79be1d2f3 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/MFAManagementView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/MFAManagementView.swift @@ -134,7 +134,6 @@ extension MFAManagementView: View { .onAppear { loadEnrolledFactors() } - // Password prompt sheet now centralized in AuthPickerView } @ViewBuilder diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/SignedInView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/SignedInView.swift index d66848437c..960ad1d376 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/SignedInView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/SignedInView.swift @@ -143,25 +143,15 @@ extension SignedInView: View { ) .presentationDetents([.medium]) } - // Password prompt sheet now centralized in AuthPickerView - .sheet(isPresented: $showEmailVerificationSent) { - VStack(spacing: 24) { - Text(authService.string.verifyEmailSheetMessage) - .font(.headline) - Button { - showEmailVerificationSent = false - } label: { - Text(authService.string.okButtonLabel) - .padding(.vertical, 8) - .frame(maxWidth: .infinity) - } - .buttonStyle(.borderedProminent) - .padding([.top, .bottom], 8) - .frame(maxWidth: .infinity) + .alert( + authService.string.verifyEmailSheetMessage, + isPresented: $showEmailVerificationSent + ) { + Button(authService.string.okButtonLabel) { + showEmailVerificationSent = false } - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) - .safeAreaPadding() - .presentationDetents([.medium]) + } message: { + Text("Please tap on the link in your email to complete verification.") } } } diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/UpdatePasswordView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/UpdatePasswordView.swift index d6ff5eea26..9e8a1cb263 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/UpdatePasswordView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/UpdatePasswordView.swift @@ -103,7 +103,7 @@ extension UpdatePasswordView: View { } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .safeAreaPadding() - .navigationTitle(authService.string.passwordRecoveryTitle) + .navigationTitle(authService.string.updatePasswordTitle) .alert( "Password Updated", isPresented: $showAlert diff --git a/e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAEnrolmentUITests.swift b/e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAEnrolmentUITests.swift index c524097952..c5563be16e 100644 --- a/e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAEnrolmentUITests.swift +++ b/e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAEnrolmentUITests.swift @@ -23,21 +23,21 @@ import XCTest final class MFAEnrollmentUITests: XCTestCase { var app: XCUIApplication! - + override func setUpWithError() throws { continueAfterFailure = false } - + override func tearDownWithError() throws { // Clean up: Terminate app if let app = app { app.terminate() } app = nil - + // Small delay between tests to allow emulator to settle Thread.sleep(forTimeInterval: 0.5) - + try super.tearDownWithError() } @@ -226,7 +226,7 @@ final class MFAEnrollmentUITests: XCTestCase { let phoneField = app.textFields["phone-number-field"] XCTAssertTrue(phoneField.waitForExistence(timeout: 10)) // Generate unique phone number using timestamp to avoid conflicts between tests - let uniqueId = Int(Date().timeIntervalSince1970 * 1000) % 1000000 + let uniqueId = Int(Date().timeIntervalSince1970 * 1000) % 1_000_000 let phoneNumberWithoutDialCode = "7\(String(format: "%09d", uniqueId))" UIPasteboard.general.string = phoneNumberWithoutDialCode phoneField.tap() @@ -258,9 +258,9 @@ final class MFAEnrollmentUITests: XCTestCase { // Paste each digit into the corresponding text field let codeDigits = Array(code) - let fields = [verificationCodeField1, verificationCodeField2, verificationCodeField3, + let fields = [verificationCodeField1, verificationCodeField2, verificationCodeField3, verificationCodeField4, verificationCodeField5, verificationCodeField6] - + for (index, digit) in codeDigits.enumerated() where index < fields.count { let field = fields[index] UIPasteboard.general.string = String(digit) diff --git a/e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/TestUtils.swift b/e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/TestUtils.swift index bef4c7f5de..b1e6e5df53 100644 --- a/e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/TestUtils.swift +++ b/e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/TestUtils.swift @@ -139,7 +139,9 @@ func createEmail() -> String { guard let http = sendResp as? HTTPURLResponse, http.statusCode == 200 else { let errorBody = String(data: sendData, encoding: .utf8) ?? "Unknown error" throw NSError(domain: "EmulatorError", code: 1, - userInfo: [NSLocalizedDescriptionKey: "Failed to send verification email: \(errorBody)"]) + userInfo: [ + NSLocalizedDescriptionKey: "Failed to send verification email: \(errorBody)", + ]) } // Add a small delay to ensure the OOB code is registered in the emulator @@ -156,12 +158,12 @@ func createEmail() -> String { // Step 2: Fetch OOB codes from emulator with retry logic let oobURL = URL(string: "\(base)/emulator/v1/projects/\(projectID)/oobCodes")! - + var codeItem: OobItem? var attempts = 0 let maxAttempts = 5 - - while codeItem == nil && attempts < maxAttempts { + + while codeItem == nil, attempts < maxAttempts { let (oobData, oobResp) = try await URLSession.shared.data(from: oobURL) guard (oobResp as? HTTPURLResponse)?.statusCode == 200 else { throw NSError(domain: "EmulatorError", code: 2, @@ -182,7 +184,7 @@ func createEmail() -> String { return d0 > d1 } .first - + if codeItem == nil { attempts += 1 if attempts < maxAttempts { @@ -190,7 +192,8 @@ func createEmail() -> String { try await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds } else { // Log available codes for debugging - let availableCodes = envelope.oobCodes.map { "Email: \($0.email), Type: \($0.requestType)" }.joined(separator: "; ") + let availableCodes = envelope.oobCodes.map { "Email: \($0.email), Type: \($0.requestType)" } + .joined(separator: "; ") throw NSError(domain: "EmulatorError", code: 3, userInfo: [ NSLocalizedDescriptionKey: "No VERIFY_EMAIL OOB code found for \(email) after \(maxAttempts) attempts. Available codes: \(availableCodes)",