Skip to content
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
22 changes: 11 additions & 11 deletions .github/workflows/swiftui-auth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -90,15 +90,15 @@ 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: |
cd ./samples/swiftui/FirebaseSwiftUIExample
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
Expand All @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -156,15 +156,15 @@ 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: |
cd ./samples/swiftui/FirebaseSwiftUIExample
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
Expand All @@ -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 \
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<Provider: AuthProviderSwift>: 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()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)")
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
}
}
Expand Down
Loading
Loading