Skip to content

[Auth] Conform 'AuthKeychainServices' to 'Sendable' #14862

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions FirebaseAuth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Unreleased
- [fixed] Synchronize internal `AuthKeychainServices` class to prevent
crashes from concurrent access. (#14835)

# 11.12.0
- [fixed] Fix a `fatalError` unenrolling from MFA. An invalid user token now throws an
`invalidUserToken` error instead of crashing. (#14663)
Expand Down
2 changes: 1 addition & 1 deletion FirebaseAuth/Sources/Swift/Auth/Auth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1625,7 +1625,7 @@ extension Auth: AuthInterop {
// MARK: Internal methods

init(app: FirebaseApp,
keychainStorageProvider: AuthKeychainStorage = AuthKeychainStorageReal(),
keychainStorageProvider: AuthKeychainStorage = AuthKeychainStorageReal.shared,
backend: AuthBackend = .init(rpcIssuer: AuthBackendRPCIssuer()),
authDispatcher: AuthDispatcher = .init()) {
self.app = app
Expand Down
5 changes: 4 additions & 1 deletion FirebaseAuth/Sources/Swift/Auth/AuthComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ class AuthComponent: NSObject, Library, ComponentLifecycleMaintainer {
// This doesn't stop any request already issued, see b/27704535

if let keychainServiceName = Auth.deleteKeychainServiceNameForAppName(app.name) {
let keychain = AuthKeychainServices(service: keychainServiceName)
let keychain = AuthKeychainServices(
service: keychainServiceName,
storage: AuthKeychainStorageReal.shared
)
let userKey = "\(app.name)_firebase_user"
try? keychain.removeData(forKey: userKey)
}
Expand Down
24 changes: 10 additions & 14 deletions FirebaseAuth/Sources/Swift/Storage/AuthKeychainServices.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

import FirebaseCoreExtension
import FirebaseCoreInternal
import Foundation

/// The prefix string for keychain item account attribute before the key.
Expand All @@ -22,16 +23,15 @@ private let kAccountPrefix = "firebase_auth_1_"

/// The utility class to manipulate data in iOS Keychain.
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
final class AuthKeychainServices {
final class AuthKeychainServices: Sendable {
/// The name of the keychain service.
let service: String
private let service: String

let keychainStorage: AuthKeychainStorage
private let keychainStorage: AuthKeychainStorage

// MARK: - Internal methods for shared keychain operations

required init(service: String = "Unset service",
storage: AuthKeychainStorage = AuthKeychainStorageReal()) {
required init(service: String = "Unset service", storage: AuthKeychainStorage) {
self.service = service
keychainStorage = storage
}
Expand Down Expand Up @@ -102,19 +102,15 @@ final class AuthKeychainServices {
/// been deleted.
///
/// This dictionary is to avoid unnecessary keychain operations against legacy items.
private var legacyEntryDeletedForKey: Set<String> = []

static func storage(identifier: String) -> Self {
return Self(service: identifier)
}
private let legacyEntryDeletedForKey = FIRAllocatedUnfairLock<Set<String>>(initialState: [])

func data(forKey key: String) throws -> Data? {
if let data = try getItemLegacy(query: genericPasswordQuery(key: key)) {
return data
}

// Check for legacy form.
if legacyEntryDeletedForKey.contains(key) {
if legacyEntryDeletedForKey.value().contains(key) {
return nil
}
if let data = try getItemLegacy(query: legacyGenericPasswordQuery(key: key)) {
Expand All @@ -124,7 +120,7 @@ final class AuthKeychainServices {
return data
} else {
// Mark legacy data as non-existing so we don't have to query it again.
legacyEntryDeletedForKey.insert(key)
legacyEntryDeletedForKey.withLock { $0.insert(key) }
return nil
}
}
Expand Down Expand Up @@ -214,12 +210,12 @@ final class AuthKeychainServices {
/// Deletes legacy item from the keychain if it is not already known to be deleted.
/// - Parameter key: The key for the item.
private func deleteLegacyItem(key: String) {
if legacyEntryDeletedForKey.contains(key) {
if legacyEntryDeletedForKey.value().contains(key) {
return
}
let query = legacyGenericPasswordQuery(key: key)
keychainStorage.delete(query: query)
legacyEntryDeletedForKey.insert(key)
legacyEntryDeletedForKey.withLock { $0.insert(key) }
}

/// Returns a keychain query of generic password to be used to manipulate key'ed value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import Foundation
/// Protocol to manage keychain updates. Tests can do a fake implementation.

@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
protocol AuthKeychainStorage {
protocol AuthKeychainStorage: Sendable {
func get(query: [String: Any], result: inout AnyObject?) -> OSStatus
func add(query: [String: Any]) -> OSStatus
func update(query: [String: Any], attributes: [String: Any]) -> OSStatus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
import Foundation

/// The utility class to update the real keychain

@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
class AuthKeychainStorageReal: AuthKeychainStorage {
final class AuthKeychainStorageReal: AuthKeychainStorage {
static let shared: AuthKeychainStorageReal = .init()

private init() {}

func get(query: [String: Any], result: inout AnyObject?) -> OSStatus {
return SecItemCopyMatching(query as CFDictionary, &result)
}
Expand Down
6 changes: 0 additions & 6 deletions FirebaseAuth/Sources/Swift/Storage/AuthUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,11 @@ private let kPersistentDomainNamePrefix = "com.google.Firebase.Auth."
/// The utility class to manage data storage in NSUserDefaults.
class AuthUserDefaults {
/// The name of the persistent domain in user defaults.

private let persistentDomainName: String

/// The backing NSUserDefaults storage for this instance.

private let storage: UserDefaults

static func storage(identifier: String) -> Self {
return Self(service: identifier)
}

required init(service: String) {
persistentDomainName = kPersistentDomainNamePrefix + service
storage = UserDefaults()
Expand Down
17 changes: 9 additions & 8 deletions FirebaseAuth/Tests/Unit/AuthKeychainServicesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ class AuthKeychainServicesTests: XCTestCase {
}

var keychain: AuthKeychainServices!
#if (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE
let storage: AuthKeychainStorage = FakeAuthKeychainStorage()
#else
let storage: AuthKeychainStorage = AuthKeychainStorageReal.shared
#endif // (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE

override func setUp() {
super.setUp()
#if (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE
keychain = AuthKeychainServices(service: Self.service, storage: FakeAuthKeychainStorage())
#else
keychain = AuthKeychainServices(service: Self.service)
#endif // (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE
keychain = AuthKeychainServices(service: Self.service, storage: storage)
}

func testReadNonexisting() throws {
Expand Down Expand Up @@ -142,7 +143,7 @@ class AuthKeychainServicesTests: XCTestCase {
}

var result: CFTypeRef?
let status = keychain.keychainStorage.get(query: query as [String: Any], result: &result)
let status = storage.get(query: query as [String: Any], result: &result)

guard let result = result as? Data, status != errSecItemNotFound else {
if let resultArray = result as? [[String: Any]],
Expand All @@ -168,7 +169,7 @@ class AuthKeychainServicesTests: XCTestCase {
if let service {
query[kSecAttrService] = service
}
XCTAssertEqual(keychain.keychainStorage.add(query: query as [String: Any]), errSecSuccess)
XCTAssertEqual(storage.add(query: query as [String: Any]), errSecSuccess)
}

private func setPassword(_ password: String?,
Expand All @@ -192,6 +193,6 @@ class AuthKeychainServicesTests: XCTestCase {
if let service {
query[kSecAttrService] = service
}
XCTAssertEqual(keychain.keychainStorage.delete(query: query as [String: Any]), errSecSuccess)
XCTAssertEqual(storage.delete(query: query as [String: Any]), errSecSuccess)
}
}
2 changes: 1 addition & 1 deletion FirebaseAuth/Tests/Unit/AuthTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
#if (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE
let keychainStorageProvider = FakeAuthKeychainStorage()
#else
let keychainStorageProvider = AuthKeychainStorageReal()
let keychainStorageProvider = AuthKeychainStorageReal.shared
#endif // (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE

// Stub the implementation to save the token refresh task for later execution.
Expand Down Expand Up @@ -87,7 +87,7 @@
return try self.rpcIssuer.respond(withJSON: ["signinMethods": allSignInMethods])
}

auth?.fetchSignInMethods(forEmail: kEmail) { signInMethods, error in

Check warning on line 90 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, macOS)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.

Check warning on line 90 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, tvOS)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.

Check warning on line 90 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, catalyst)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.

Check warning on line 90 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, watchOS)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.

Check warning on line 90 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, visionOS)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.
// 4. After the response triggers the callback, verify the returned signInMethods.
XCTAssertTrue(Thread.isMainThread)
XCTAssertEqual(signInMethods, allSignInMethods)
Expand All @@ -108,7 +108,7 @@
let message = "TOO_MANY_ATTEMPTS_TRY_LATER"
return try self.rpcIssuer.respond(serverErrorMessage: message)
}
auth?.fetchSignInMethods(forEmail: kEmail) { signInMethods, error in

Check warning on line 111 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, macOS)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.

Check warning on line 111 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, tvOS)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.

Check warning on line 111 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, catalyst)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.

Check warning on line 111 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, watchOS)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.

Check warning on line 111 in FirebaseAuth/Tests/Unit/AuthTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, visionOS)

'fetchSignInMethods(forEmail:completion:)' is deprecated: `fetchSignInMethods` is deprecated and will be removed in a future release. This method returns an empty list when Email Enumeration Protection is enabled.
XCTAssertTrue(Thread.isMainThread)
XCTAssertNil(signInMethods)
let rpcError = (error as? NSError)!
Expand Down
29 changes: 15 additions & 14 deletions FirebaseAuth/Tests/Unit/Fakes/FakeAuthKeychainStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,28 @@
// limitations under the License.

@testable import FirebaseAuth
import FirebaseCoreInternal
import Foundation
import XCTest

/** @class AuthKeychainStorage
@brief The utility class to update the real keychain
*/
/// The utility class to update the real keychain
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
class FakeAuthKeychainStorage: AuthKeychainStorage {
final class FakeAuthKeychainStorage: AuthKeychainStorage {
// Fake Keychain. It's a dictionary, keyed by service name, for each key-value store dictionary
private var fakeKeychain: [String: [String: Any]] = [:]
private let fakeKeychain = FIRAllocatedUnfairLock<[String: [String: Any]]>(initialState: [:])

private var fakeLegacyKeychain: [String: Any] = [:]
private let fakeLegacyKeychain = FIRAllocatedUnfairLock<[String: Any]>(initialState: [:])

func get(query: [String: Any], result: inout AnyObject?) -> OSStatus {
if let service = queryService(query) {
guard let value = fakeKeychain[service]?[queryKey(query)] else {
guard let value = fakeKeychain.value()[service]?[queryKey(query)] else {
return errSecItemNotFound
}
let returnArrayofDictionary = [[kSecValueData as String: value]]
result = returnArrayofDictionary as AnyObject
return noErr
} else {
guard let value = fakeLegacyKeychain[queryKey(query)] else {
guard let value = fakeLegacyKeychain.value()[queryKey(query)] else {
return errSecItemNotFound
}
let returnArrayofDictionary = [[kSecValueData as String: value]]
Expand All @@ -46,9 +45,9 @@ class FakeAuthKeychainStorage: AuthKeychainStorage {

func add(query: [String: Any]) -> OSStatus {
if let service = queryService(query) {
fakeKeychain[service]?[queryKey(query)] = query[kSecValueData as String]
fakeKeychain.withLock { $0[service]?[queryKey(query)] = query[kSecValueData as String] }
} else {
fakeLegacyKeychain[queryKey(query)] = query[kSecValueData as String]
fakeLegacyKeychain.withLock { $0[queryKey(query)] = query[kSecValueData as String] }
}
return noErr
}
Expand All @@ -59,9 +58,9 @@ class FakeAuthKeychainStorage: AuthKeychainStorage {

@discardableResult func delete(query: [String: Any]) -> OSStatus {
if let service = queryService(query) {
fakeKeychain[service]?[queryKey(query)] = nil
fakeKeychain.withLock { $0[service]?[queryKey(query)] = nil }
} else {
fakeLegacyKeychain[queryKey(query)] = nil
fakeLegacyKeychain.withLock { $0[queryKey(query)] = nil }
}
return noErr
}
Expand All @@ -79,8 +78,10 @@ class FakeAuthKeychainStorage: AuthKeychainStorage {
guard let service = query[kSecAttrService as String] as? String else {
return nil
}
if fakeKeychain[service] == nil {
fakeKeychain[service] = [:]
fakeKeychain.withLock { fakeKeychain in
if fakeKeychain[service] == nil {
fakeKeychain[service] = [:]
}
}
return service
}
Expand Down
2 changes: 1 addition & 1 deletion FirebaseAuth/Tests/Unit/UserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
#if (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE
let keychainStorageProvider = FakeAuthKeychainStorage()
#else
let keychainStorageProvider = AuthKeychainStorageReal()
let keychainStorageProvider = AuthKeychainStorageReal.shared
#endif // (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE
auth = Auth(
app: FirebaseApp.app(name: "test-UserTests")!,
Expand Down Expand Up @@ -413,7 +413,7 @@
self.rpcIssuer.respondBlock = {
try self.rpcIssuer.respond(serverErrorMessage: "INVALID_EMAIL")
}
user.updateEmail(to: self.kNewEmail) { rawError in

Check warning on line 416 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, macOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 416 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, tvOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 416 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, iOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 416 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, watchOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 416 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, visionOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 416 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-14, Xcode_16.2, iOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.invalidEmail.rawValue)
Expand All @@ -439,7 +439,7 @@
self.rpcIssuer.respondBlock = {
try self.rpcIssuer.respond(serverErrorMessage: "INVALID_ID_TOKEN")
}
user.updateEmail(to: self.kNewEmail) { rawError in

Check warning on line 442 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, macOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 442 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, tvOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 442 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, iOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 442 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, watchOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 442 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, visionOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 442 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-14, Xcode_16.2, iOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.
XCTAssertTrue(Thread.isMainThread)
let error = try! XCTUnwrap(rawError)
XCTAssertEqual((error as NSError).code, AuthErrorCode.invalidUserToken.rawValue)
Expand Down Expand Up @@ -1651,7 +1651,7 @@
"refreshToken": self.kRefreshToken])
}
if changeEmail {
user.updateEmail(to: kNewEmail) { error in

Check warning on line 1654 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, macOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 1654 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, tvOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 1654 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, iOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 1654 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, watchOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 1654 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.3, visionOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.

Check warning on line 1654 in FirebaseAuth/Tests/Unit/UserTests.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-14, Xcode_16.2, iOS)

'updateEmail(to:completion:)' is deprecated: `updateEmail` is deprecated and will be removed in a future release. Use sendEmailVerification(beforeUpdatingEmail:) instead.
XCTAssertNil(error)
XCTAssertEqual(user.email, self.kNewEmail)
XCTAssertEqual(user.displayName, self.kNewDisplayName)
Expand Down
Loading