diff --git a/.github/workflows/swiftui-auth.yml b/.github/workflows/swiftui-auth.yml index 7faaacd95c..44f17a59c5 100644 --- a/.github/workflows/swiftui-auth.yml +++ b/.github/workflows/swiftui-auth.yml @@ -25,7 +25,7 @@ jobs: # Package Unit Tests (standalone, no emulator needed) unit-tests: name: Package Unit Tests - runs-on: macos-15 + runs-on: macos-26 timeout-minutes: 15 steps: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 @@ -34,14 +34,14 @@ jobs: run: gem install xcpretty - name: Select Xcode version - run: sudo xcode-select -switch /Applications/Xcode_16.4.app/Contents/Developer + run: sudo xcode-select -switch /Applications/Xcode_26.0.1.app/Contents/Developer - name: Run FirebaseSwiftUI Package Unit Tests run: | set -o pipefail xcodebuild test \ -scheme FirebaseUI-Package \ - -destination 'platform=iOS Simulator,name=iPhone 16 Pro' \ + -destination 'platform=iOS Simulator,name=iPhone 17' \ -enableCodeCoverage YES \ -resultBundlePath FirebaseSwiftUIPackageTests.xcresult | tee FirebaseSwiftUIPackageTests.log | xcpretty --test --color --simple @@ -62,7 +62,7 @@ jobs: # Integration Tests (requires emulator) integration-tests: name: Integration Tests - runs-on: macos-15 + runs-on: macos-26 timeout-minutes: 20 steps: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 @@ -90,7 +90,7 @@ jobs: run: gem install xcpretty - name: Select Xcode version - run: sudo xcode-select -switch /Applications/Xcode_16.4.app/Contents/Developer + run: sudo xcode-select -switch /Applications/Xcode_26.0.1.app/Contents/Developer - name: Build for Integration Tests run: | @@ -98,7 +98,7 @@ jobs: set -o pipefail xcodebuild build-for-testing \ -scheme FirebaseSwiftUIExampleTests \ - -destination 'platform=iOS Simulator,name=iPhone 16 Pro' \ + -destination 'platform=iOS Simulator,name=iPhone 17' \ -enableCodeCoverage YES | xcpretty --color --simple - name: Run Integration Tests @@ -107,7 +107,7 @@ jobs: set -o pipefail xcodebuild test-without-building \ -scheme FirebaseSwiftUIExampleTests \ - -destination 'platform=iOS Simulator,name=iPhone 16 Pro' \ + -destination 'platform=iOS Simulator,name=iPhone 17' \ -enableCodeCoverage YES \ -resultBundlePath FirebaseSwiftUIExampleTests.xcresult | tee FirebaseSwiftUIExampleTests.log | xcpretty --test --color --simple @@ -128,7 +128,7 @@ jobs: # UI Tests (requires emulator) ui-tests: name: UI Tests - runs-on: macos-15 + runs-on: macos-26 timeout-minutes: 40 steps: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 @@ -156,7 +156,7 @@ jobs: run: gem install xcpretty - name: Select Xcode version - run: sudo xcode-select -switch /Applications/Xcode_16.4.app/Contents/Developer + run: sudo xcode-select -switch /Applications/Xcode_26.0.1.app/Contents/Developer - name: Build for UI Tests run: | @@ -164,7 +164,7 @@ jobs: set -o pipefail xcodebuild build-for-testing \ -scheme FirebaseSwiftUIExampleUITests \ - -destination 'platform=iOS Simulator,name=iPhone 16 Pro' \ + -destination 'platform=iOS Simulator,name=iPhone 17' \ -enableCodeCoverage YES | xcpretty --color --simple - name: Run UI Tests @@ -173,7 +173,7 @@ jobs: set -o pipefail xcodebuild test-without-building \ -scheme FirebaseSwiftUIExampleUITests \ - -destination 'platform=iOS Simulator,name=iPhone 16 Pro' \ + -destination 'platform=iOS Simulator,name=iPhone 17' \ -parallel-testing-enabled YES \ -maximum-concurrent-test-simulator-destinations 2 \ -enableCodeCoverage YES \ diff --git a/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Services/AccountService+Apple.swift b/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Services/AccountService+Apple.swift deleted file mode 100644 index 6974a1d902..0000000000 --- a/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Services/AccountService+Apple.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// AccountService+Apple.swift -// FirebaseUI -// -// Created by Russell Wheatley on 21/10/2025. -// - -@preconcurrency import FirebaseAuth -import FirebaseAuthSwiftUI -import Observation - -protocol AppleOperationReauthentication { - var appleProvider: AppleProviderSwift { get } -} - -extension AppleOperationReauthentication { - @MainActor func reauthenticate() async throws { - guard let user = Auth.auth().currentUser else { - throw AuthServiceError.reauthenticationRequired("No user currently signed-in") - } - - do { - let credential = try await appleProvider.createAuthCredential() - try await user.reauthenticate(with: credential) - } catch { - throw AuthServiceError.signInFailed(underlying: error) - } - } -} - -@MainActor -class AppleDeleteUserOperation: AuthenticatedOperation, - @preconcurrency AppleOperationReauthentication { - let appleProvider: AppleProviderSwift - init(appleProvider: AppleProviderSwift) { - self.appleProvider = appleProvider - } - - func callAsFunction(on user: User) async throws { - try await callAsFunction(on: user) { - try await user.delete() - } - } -} diff --git a/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Services/AppleProviderAuthUI.swift b/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Services/AppleProviderAuthUI.swift index 76dacb5baa..a3d829e532 100644 --- a/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Services/AppleProviderAuthUI.swift +++ b/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Services/AppleProviderAuthUI.swift @@ -114,7 +114,7 @@ extension AuthenticateWithAppleDialog: ASAuthorizationControllerDelegate { // MARK: - Apple Provider Swift -public class AppleProviderSwift: AuthProviderSwift, DeleteUserSwift { +public class AppleProviderSwift: AuthProviderSwift { public let scopes: [ASAuthorization.Scope] let providerId = "apple.com" @@ -139,7 +139,7 @@ public class AppleProviderSwift: AuthProviderSwift, DeleteUserSwift { } public func deleteUser(user: User) async throws { - let operation = AppleDeleteUserOperation(appleProvider: self) + let operation = ProviderDeleteUserOperation(provider: self) try await operation(on: user) } } diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService+Email.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService+Email.swift index 1e0db52283..2d9848cdc7 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService+Email.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService+Email.swift @@ -15,13 +15,13 @@ @preconcurrency import FirebaseAuth import Observation +@MainActor protocol EmailPasswordOperationReauthentication { var passwordPrompt: PasswordPromptCoordinator { get } } extension EmailPasswordOperationReauthentication { - // TODO: - @MainActor because User is non-sendable. Might change this once User is sendable in firebase-ios-sdk - @MainActor func reauthenticate() async throws { + func reauthenticate() async throws { guard let user = Auth.auth().currentUser else { throw AuthServiceError.reauthenticationRequired("No user currently signed-in") } @@ -34,7 +34,7 @@ extension EmailPasswordOperationReauthentication { let password = try await passwordPrompt.confirmPassword() let credential = EmailAuthProvider.credential(withEmail: email, password: password) - try await Auth.auth().currentUser?.reauthenticate(with: credential) + _ = try await Auth.auth().currentUser?.reauthenticate(with: credential) } catch { throw AuthServiceError.signInFailed(underlying: error) } diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService.swift index 8ad6cbe342..954eccf1b4 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService.swift @@ -33,7 +33,7 @@ public protocol AuthenticatedOperation { public extension AuthenticatedOperation { func callAsFunction(on _: User, - _ performOperation: () async throws -> Void) async throws { + _ performOperation: @MainActor () async throws -> Void) async throws { do { try await performOperation() } catch let error as NSError where error.requiresReauthentication { @@ -45,3 +45,41 @@ public extension AuthenticatedOperation { } } } + +@MainActor +public protocol ProviderOperationReauthentication { + var authProvider: AuthProviderSwift { get } +} + +public extension ProviderOperationReauthentication { + func reauthenticate() async throws { + guard let user = Auth.auth().currentUser else { + throw AuthServiceError.reauthenticationRequired("No user currently signed-in") + } + + do { + let credential = try await authProvider.createAuthCredential() + _ = try await user.reauthenticate(with: credential) + } catch { + throw AuthServiceError.signInFailed(underlying: error) + } + } +} + +@MainActor +public class ProviderDeleteUserOperation: AuthenticatedOperation, + @preconcurrency ProviderOperationReauthentication { + let provider: Provider + + public var authProvider: AuthProviderSwift { provider } + + public init(provider: Provider) { + self.provider = provider + } + + public func callAsFunction(on user: User) async throws { + try await callAsFunction(on: user) { + try await user.delete() + } + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 4c124a695c..bc2e2fa626 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -17,6 +17,7 @@ import SwiftUI public protocol AuthProviderSwift { @MainActor func createAuthCredential() async throws -> AuthCredential + @MainActor func deleteUser(user: User) async throws } public protocol AuthProviderUI { @@ -25,10 +26,6 @@ public protocol AuthProviderUI { var provider: AuthProviderSwift { get } } -public protocol DeleteUserSwift { - @MainActor func deleteUser(user: User) async throws -} - public protocol PhoneAuthProviderSwift: AuthProviderSwift { @MainActor func verifyPhoneNumber(phoneNumber: String) async throws -> String } @@ -282,13 +279,12 @@ public extension AuthService { let operation = EmailPasswordDeleteUserOperation(passwordPrompt: passwordPrompt) try await operation(on: user) } else { - // Find provider by matching ID and ensure it can delete users - guard let matchingProvider = providers.first(where: { $0.id == providerId }), - let provider = matchingProvider.provider as? DeleteUserSwift else { + // Find provider by matching ID + guard let matchingProvider = providers.first(where: { $0.id == providerId }) else { throw AuthServiceError.providerNotFound("No provider found for \(providerId)") } - try await provider.deleteUser(user: user) + try await matchingProvider.provider.deleteUser(user: user) } } } catch { @@ -723,10 +719,10 @@ public extension AuthService { } let password = try await passwordPrompt.confirmPassword() let credential = EmailAuthProvider.credential(withEmail: email, password: password) - try await user.reauthenticate(with: credential) + _ = try await user.reauthenticate(with: credential) } else if let matchingProvider = providers.first(where: { $0.id == providerId }) { let credential = try await matchingProvider.provider.createAuthCredential() - try await user.reauthenticate(with: credential) + _ = try await user.reauthenticate(with: credential) } else { throw AuthServiceError.providerNotFound("No provider found for \(providerId)") } diff --git a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/AccountService+Facebook.swift b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/AccountService+Facebook.swift deleted file mode 100644 index 2100d08467..0000000000 --- a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/AccountService+Facebook.swift +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2025 Google LLC -// -// 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. - -// -// AccountService+Facebook.swift -// FirebaseUI -// -// Created by Russell Wheatley on 14/05/2025. -// - -@preconcurrency import FirebaseAuth -import FirebaseAuthSwiftUI -import Observation - -protocol FacebookOperationReauthentication { - var facebookProvider: FacebookProviderSwift { get } -} - -extension FacebookOperationReauthentication { - @MainActor func reauthenticate() async throws { - guard let user = Auth.auth().currentUser else { - throw AuthServiceError.reauthenticationRequired("No user currently signed-in") - } - - do { - let credential = try await facebookProvider.createAuthCredential() - try await user.reauthenticate(with: credential) - } catch { - throw AuthServiceError.signInFailed(underlying: error) - } - } -} - -@MainActor -class FacebookDeleteUserOperation: AuthenticatedOperation, - @preconcurrency FacebookOperationReauthentication { - let facebookProvider: FacebookProviderSwift - init(facebookProvider: FacebookProviderSwift) { - self.facebookProvider = facebookProvider - } - - func callAsFunction(on user: User) async throws { - try await callAsFunction(on: user) { - try await user.delete() - } - } -} diff --git a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderAuthUI.swift b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderAuthUI.swift index be4194e49e..f123f04229 100644 --- a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderAuthUI.swift +++ b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderAuthUI.swift @@ -19,7 +19,7 @@ import FirebaseAuth import FirebaseAuthSwiftUI import SwiftUI -public class FacebookProviderSwift: AuthProviderSwift, DeleteUserSwift { +public class FacebookProviderSwift: AuthProviderSwift { let scopes: [String] let providerId = "facebook.com" private let loginManager = LoginManager() @@ -118,7 +118,7 @@ public class FacebookProviderSwift: AuthProviderSwift, DeleteUserSwift { } public func deleteUser(user: User) async throws { - let operation = FacebookDeleteUserOperation(facebookProvider: self) + let operation = ProviderDeleteUserOperation(provider: self) try await operation(on: user) } } diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/AccountService+Google.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/AccountService+Google.swift deleted file mode 100644 index c651d66bab..0000000000 --- a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/AccountService+Google.swift +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2025 Google LLC -// -// 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. - -// -// AccountService+Google.swift -// FirebaseUI -// -// Created by Russell Wheatley on 22/05/2025. -// - -// -// AccountService+Facebook.swift -// FirebaseUI -// -// Created by Russell Wheatley on 14/05/2025. -// - -@preconcurrency import FirebaseAuth -import FirebaseAuthSwiftUI -import Observation - -protocol GoogleOperationReauthentication { - var googleProvider: GoogleProviderSwift { get } -} - -extension GoogleOperationReauthentication { - @MainActor func reauthenticate() async throws { - guard let user = Auth.auth().currentUser else { - throw AuthServiceError.reauthenticationRequired("No user currently signed-in") - } - - do { - let credential = try await googleProvider.createAuthCredential() - try await user.reauthenticate(with: credential) - - } catch { - throw AuthServiceError.signInFailed(underlying: error) - } - } -} - -@MainActor -class GoogleDeleteUserOperation: AuthenticatedOperation, - @preconcurrency GoogleOperationReauthentication { - let googleProvider: GoogleProviderSwift - init(googleProvider: GoogleProviderSwift) { - self.googleProvider = googleProvider - } - - func callAsFunction(on user: User) async throws { - try await callAsFunction(on: user) { - try await user.delete() - } - } -} diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderAuthUI.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderAuthUI.swift index 06ad5ca39f..c3edb91956 100644 --- a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderAuthUI.swift +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderAuthUI.swift @@ -19,7 +19,7 @@ import GoogleSignIn import GoogleSignInSwift import SwiftUI -public class GoogleProviderSwift: AuthProviderSwift, DeleteUserSwift { +public class GoogleProviderSwift: AuthProviderSwift { let scopes: [String] let clientID: String let providerId = "google.com" @@ -70,7 +70,7 @@ public class GoogleProviderSwift: AuthProviderSwift, DeleteUserSwift { } public func deleteUser(user: User) async throws { - let operation = GoogleDeleteUserOperation(googleProvider: self) + let operation = ProviderDeleteUserOperation(provider: self) try await operation(on: user) } } diff --git a/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Services/AccountService+OAuth.swift b/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Services/AccountService+OAuth.swift deleted file mode 100644 index 44b66b4403..0000000000 --- a/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Services/AccountService+OAuth.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// AccountService+OAuth.swift -// FirebaseUI -// -// Created by Russell Wheatley on 21/10/2025. -// - -@preconcurrency import FirebaseAuth -import FirebaseAuthSwiftUI -import Observation - -protocol OAuthOperationReauthentication { - var oauthProvider: OAuthProviderSwift { get } -} - -extension OAuthOperationReauthentication { - @MainActor func reauthenticate() async throws { - guard let user = Auth.auth().currentUser else { - throw AuthServiceError.reauthenticationRequired("No user currently signed-in") - } - - do { - let credential = try await oauthProvider.createAuthCredential() - try await user.reauthenticate(with: credential) - } catch { - throw AuthServiceError.signInFailed(underlying: error) - } - } -} - -@MainActor -class OAuthDeleteUserOperation: AuthenticatedOperation, - @preconcurrency OAuthOperationReauthentication { - let oauthProvider: OAuthProviderSwift - init(oauthProvider: OAuthProviderSwift) { - self.oauthProvider = oauthProvider - } - - func callAsFunction(on user: User) async throws { - try await callAsFunction(on: user) { - try await user.delete() - } - } -} diff --git a/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Services/OAuthProviderSwift.swift b/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Services/OAuthProviderSwift.swift index feb3f9062c..0a39a5fd1d 100644 --- a/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Services/OAuthProviderSwift.swift +++ b/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Services/OAuthProviderSwift.swift @@ -18,7 +18,7 @@ import FirebaseCore import SwiftUI /// Configuration for a generic OAuth provider -public class OAuthProviderSwift: AuthProviderSwift, DeleteUserSwift { +public class OAuthProviderSwift: AuthProviderSwift { public let providerId: String public let scopes: [String] public let customParameters: [String: String] @@ -115,7 +115,7 @@ public class OAuthProviderSwift: AuthProviderSwift, DeleteUserSwift { } public func deleteUser(user: User) async throws { - let operation = OAuthDeleteUserOperation(oauthProvider: self) + let operation = ProviderDeleteUserOperation(provider: self) try await operation(on: user) } } diff --git a/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderAuthUI.swift b/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderAuthUI.swift index c1c3d89695..77b040b2d1 100644 --- a/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderAuthUI.swift +++ b/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderAuthUI.swift @@ -63,11 +63,16 @@ public class PhoneProviderSwift: PhoneAuthProviderSwift { presentingViewController.present(hostingController, animated: true) } } + + public func deleteUser(user: User) async throws { + let operation = ProviderDeleteUserOperation(provider: self) + try await operation(on: user) + } } public class PhoneAuthProviderAuthUI: AuthProviderUI { public var provider: AuthProviderSwift - public let id: String = "phone.com" + public let id: String = "phone" public init(provider: PhoneAuthProviderSwift? = nil) { self.provider = provider ?? PhoneProviderSwift() diff --git a/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Services/AccountService+Twitter.swift b/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Services/AccountService+Twitter.swift deleted file mode 100644 index 89b35fec96..0000000000 --- a/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Services/AccountService+Twitter.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// AccountService+Twitter.swift -// FirebaseUI -// -// Created by Russell Wheatley on 10/10/2025. -// - -@preconcurrency import FirebaseAuth -import FirebaseAuthSwiftUI -import Observation - -protocol TwitterOperationReauthentication { - var twitterProvider: TwitterProviderSwift { get } -} - -extension TwitterOperationReauthentication { - @MainActor func reauthenticate() async throws { - guard let user = Auth.auth().currentUser else { - throw AuthServiceError.reauthenticationRequired("No user currently signed-in") - } - - do { - let credential = try await twitterProvider.createAuthCredential() - try await user.reauthenticate(with: credential) - } catch { - throw AuthServiceError.signInFailed(underlying: error) - } - } -} - -@MainActor -class TwitterDeleteUserOperation: AuthenticatedOperation, - @preconcurrency TwitterOperationReauthentication { - let twitterProvider: TwitterProviderSwift - init(twitterProvider: TwitterProviderSwift) { - self.twitterProvider = twitterProvider - } - - func callAsFunction(on user: User) async throws { - try await callAsFunction(on: user) { - try await user.delete() - } - } -} diff --git a/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Services/TwitterProviderAuthUI.swift b/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Services/TwitterProviderAuthUI.swift index 627d8de1b9..693d72f0e6 100644 --- a/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Services/TwitterProviderAuthUI.swift +++ b/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Services/TwitterProviderAuthUI.swift @@ -17,7 +17,7 @@ import FirebaseAuthSwiftUI import FirebaseCore import SwiftUI -public class TwitterProviderSwift: AuthProviderSwift, DeleteUserSwift { +public class TwitterProviderSwift: AuthProviderSwift { public let scopes: [String] let providerId = "twitter.com" @@ -44,7 +44,7 @@ public class TwitterProviderSwift: AuthProviderSwift, DeleteUserSwift { } public func deleteUser(user: User) async throws { - let operation = TwitterDeleteUserOperation(twitterProvider: self) + let operation = ProviderDeleteUserOperation(provider: self) try await operation(on: user) } } diff --git a/Package.swift b/Package.swift index 3ab09585ea..488b32e7d2 100644 --- a/Package.swift +++ b/Package.swift @@ -272,12 +272,18 @@ let package = Package( path: "FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources", resources: [ .process("Strings"), + ], + swiftSettings: [ + .swiftLanguageVersion(.v6), ] ), .testTarget( name: "FirebaseAuthSwiftUITests", dependencies: ["FirebaseAuthSwiftUI"], - path: "FirebaseSwiftUI/FirebaseAuthSwiftUI/Tests/" + path: "FirebaseSwiftUI/FirebaseAuthSwiftUI/Tests/", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .target( name: "FirebaseGoogleSwiftUI", @@ -286,12 +292,18 @@ let package = Package( .product(name: "GoogleSignIn", package: "GoogleSignIn-iOS"), .product(name: "GoogleSignInSwift", package: "GoogleSignIn-iOS"), ], - path: "FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources" + path: "FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .testTarget( name: "FirebaseGoogleSwiftUITests", dependencies: ["FirebaseGoogleSwiftUI"], - path: "FirebaseSwiftUI/FirebaseGoogleSwiftUI/Tests/" + path: "FirebaseSwiftUI/FirebaseGoogleSwiftUI/Tests/", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .target( name: "FirebaseFacebookSwiftUI", @@ -300,24 +312,36 @@ let package = Package( .product(name: "FacebookLogin", package: "facebook-ios-sdk"), .product(name: "FacebookCore", package: "facebook-ios-sdk"), ], - path: "FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources" + path: "FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .testTarget( name: "FirebaseFacebookSwiftUITests", dependencies: ["FirebaseFacebookSwiftUI"], - path: "FirebaseSwiftUI/FirebaseFacebookSwiftUI/Tests/" + path: "FirebaseSwiftUI/FirebaseFacebookSwiftUI/Tests/", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .target( name: "FirebasePhoneAuthSwiftUI", dependencies: [ "FirebaseAuthSwiftUI", ], - path: "FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources" + path: "FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .testTarget( name: "FirebasePhoneAuthSwiftUITests", dependencies: ["FirebasePhoneAuthSwiftUI"], - path: "FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Tests/" + path: "FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Tests/", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .target( name: "FirebaseTwitterSwiftUI", @@ -327,24 +351,36 @@ let package = Package( path: "FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources", resources: [ .process("Resources") + ], + swiftSettings: [ + .swiftLanguageVersion(.v6), ] ), .testTarget( name: "FirebaseTwitterSwiftUITests", dependencies: ["FirebaseTwitterSwiftUI"], - path: "FirebaseSwiftUI/FirebaseTwitterSwiftUI/Tests/" + path: "FirebaseSwiftUI/FirebaseTwitterSwiftUI/Tests/", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .target( name: "FirebaseAppleSwiftUI", dependencies: [ "FirebaseAuthSwiftUI", ], - path: "FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources" + path: "FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .testTarget( name: "FirebaseAppleSwiftUITests", dependencies: ["FirebaseAppleSwiftUI"], - path: "FirebaseSwiftUI/FirebaseAppleSwiftUI/Tests/" + path: "FirebaseSwiftUI/FirebaseAppleSwiftUI/Tests/", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), .target( name: "FirebaseOAuthSwiftUI", @@ -354,12 +390,18 @@ let package = Package( path: "FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources", resources: [ .process("Resources") + ], + swiftSettings: [ + .swiftLanguageVersion(.v6), ] ), .testTarget( name: "FirebaseOAuthSwiftUITests", dependencies: ["FirebaseOAuthSwiftUI"], - path: "FirebaseSwiftUI/FirebaseOAuthSwiftUI/Tests/" + path: "FirebaseSwiftUI/FirebaseOAuthSwiftUI/Tests/", + swiftSettings: [ + .swiftLanguageVersion(.v6), + ] ), ] ) diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj index e0c6a0533d..abec964899 100644 --- a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj @@ -481,7 +481,7 @@ PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebase.auth.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -514,7 +514,7 @@ PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebase.auth.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -532,7 +532,7 @@ PRODUCT_BUNDLE_IDENTIFIER = io.invertase.testing.FirebaseSwiftUIExampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FirebaseSwiftUIExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FirebaseSwiftUIExample"; }; @@ -551,7 +551,7 @@ PRODUCT_BUNDLE_IDENTIFIER = io.invertase.testing.FirebaseSwiftUIExampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FirebaseSwiftUIExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FirebaseSwiftUIExample"; }; @@ -568,7 +568,7 @@ PRODUCT_BUNDLE_IDENTIFIER = io.invertase.testing.FirebaseSwiftUIExampleUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = FirebaseSwiftUIExample; }; @@ -585,7 +585,7 @@ PRODUCT_BUNDLE_IDENTIFIER = io.invertase.testing.FirebaseSwiftUIExampleUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = FirebaseSwiftUIExample; }; diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleTests/TestHarness.swift b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleTests/TestHarness.swift index 7c81662457..7645e56d03 100644 --- a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleTests/TestHarness.swift +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleTests/TestHarness.swift @@ -15,6 +15,7 @@ func configureFirebaseIfNeeded() { } } +@MainActor private var hasCheckedEmulatorAvailability = false @MainActor @@ -62,6 +63,7 @@ func createEmail() -> String { return "\(before)@\(after).com" } +@MainActor func waitForStateChange(timeout: TimeInterval = 10.0, condition: @escaping () -> Bool) async throws { let startTime = Date() diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAResolutionUITests.swift b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAResolutionUITests.swift index ef93e71984..93eff325ee 100644 --- a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAResolutionUITests.swift +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAResolutionUITests.swift @@ -121,6 +121,7 @@ final class MFAResolutionUITests: XCTestCase { /// - phoneNumber: The phone number to enroll for SMS MFA (e.g., "+15551234567") /// - displayName: Optional display name for the MFA factor /// - Returns: True if MFA was successfully enabled, false otherwise + @MainActor private func enableSMSMFAViaEmulator( idToken: String, phoneNumber: String, @@ -261,6 +262,7 @@ final class MFAResolutionUITests: XCTestCase { /// - Parameters: /// - phoneNumber: The phone number to retrieve the code for /// - codeType: The type of code - "enrollment" for MFA enrollment, "verification" for phone verification during resolution + @MainActor private func getSMSVerificationCode(for phoneNumber: String, codeType: String = "enrollment") async -> String? { let emulatorUrl = "http://127.0.0.1:9099/emulator/v1/projects/flutterfire-e2e-tests/verificationCodes" @@ -336,6 +338,7 @@ final class MFAResolutionUITests: XCTestCase { /// - email: The user's email address /// - password: The user's password (defaults to "123456") /// - Returns: The user's ID token, or nil if the sign-in failed + @MainActor private func getIDTokenFromEmulator(email: String, password: String = "123456") async -> String? { let signInUrl = "http://127.0.0.1:9099/identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=fake-api-key" @@ -379,6 +382,7 @@ final class MFAResolutionUITests: XCTestCase { } } + @MainActor private func signUpUser(email: String, password: String = "12345678") async throws { // Create user via Auth Emulator REST API let url = URL(string: "http://127.0.0.1:9099/identitytoolkit.googleapis.com/v1/accounts:signUp?key=fake-api-key")!