Skip to content

Commit 4803efa

Browse files
Merge pull request #1286 from firebase/account-service
2 parents ad1f667 + 598e918 commit 4803efa

File tree

19 files changed

+140
-309
lines changed

19 files changed

+140
-309
lines changed

.github/workflows/swiftui-auth.yml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
# Package Unit Tests (standalone, no emulator needed)
2626
unit-tests:
2727
name: Package Unit Tests
28-
runs-on: macos-15
28+
runs-on: macos-26
2929
timeout-minutes: 15
3030
steps:
3131
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
@@ -34,14 +34,14 @@ jobs:
3434
run: gem install xcpretty
3535

3636
- name: Select Xcode version
37-
run: sudo xcode-select -switch /Applications/Xcode_16.4.app/Contents/Developer
37+
run: sudo xcode-select -switch /Applications/Xcode_26.0.1.app/Contents/Developer
3838

3939
- name: Run FirebaseSwiftUI Package Unit Tests
4040
run: |
4141
set -o pipefail
4242
xcodebuild test \
4343
-scheme FirebaseUI-Package \
44-
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
44+
-destination 'platform=iOS Simulator,name=iPhone 17' \
4545
-enableCodeCoverage YES \
4646
-resultBundlePath FirebaseSwiftUIPackageTests.xcresult | tee FirebaseSwiftUIPackageTests.log | xcpretty --test --color --simple
4747
@@ -62,7 +62,7 @@ jobs:
6262
# Integration Tests (requires emulator)
6363
integration-tests:
6464
name: Integration Tests
65-
runs-on: macos-15
65+
runs-on: macos-26
6666
timeout-minutes: 20
6767
steps:
6868
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
@@ -90,15 +90,15 @@ jobs:
9090
run: gem install xcpretty
9191

9292
- name: Select Xcode version
93-
run: sudo xcode-select -switch /Applications/Xcode_16.4.app/Contents/Developer
93+
run: sudo xcode-select -switch /Applications/Xcode_26.0.1.app/Contents/Developer
9494

9595
- name: Build for Integration Tests
9696
run: |
9797
cd ./samples/swiftui/FirebaseSwiftUIExample
9898
set -o pipefail
9999
xcodebuild build-for-testing \
100100
-scheme FirebaseSwiftUIExampleTests \
101-
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
101+
-destination 'platform=iOS Simulator,name=iPhone 17' \
102102
-enableCodeCoverage YES | xcpretty --color --simple
103103
104104
- name: Run Integration Tests
@@ -107,7 +107,7 @@ jobs:
107107
set -o pipefail
108108
xcodebuild test-without-building \
109109
-scheme FirebaseSwiftUIExampleTests \
110-
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
110+
-destination 'platform=iOS Simulator,name=iPhone 17' \
111111
-enableCodeCoverage YES \
112112
-resultBundlePath FirebaseSwiftUIExampleTests.xcresult | tee FirebaseSwiftUIExampleTests.log | xcpretty --test --color --simple
113113
@@ -128,7 +128,7 @@ jobs:
128128
# UI Tests (requires emulator)
129129
ui-tests:
130130
name: UI Tests
131-
runs-on: macos-15
131+
runs-on: macos-26
132132
timeout-minutes: 40
133133
steps:
134134
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
@@ -156,15 +156,15 @@ jobs:
156156
run: gem install xcpretty
157157

158158
- name: Select Xcode version
159-
run: sudo xcode-select -switch /Applications/Xcode_16.4.app/Contents/Developer
159+
run: sudo xcode-select -switch /Applications/Xcode_26.0.1.app/Contents/Developer
160160

161161
- name: Build for UI Tests
162162
run: |
163163
cd ./samples/swiftui/FirebaseSwiftUIExample
164164
set -o pipefail
165165
xcodebuild build-for-testing \
166166
-scheme FirebaseSwiftUIExampleUITests \
167-
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
167+
-destination 'platform=iOS Simulator,name=iPhone 17' \
168168
-enableCodeCoverage YES | xcpretty --color --simple
169169
170170
- name: Run UI Tests
@@ -173,7 +173,7 @@ jobs:
173173
set -o pipefail
174174
xcodebuild test-without-building \
175175
-scheme FirebaseSwiftUIExampleUITests \
176-
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
176+
-destination 'platform=iOS Simulator,name=iPhone 17' \
177177
-parallel-testing-enabled YES \
178178
-maximum-concurrent-test-simulator-destinations 2 \
179179
-enableCodeCoverage YES \

FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Services/AccountService+Apple.swift

Lines changed: 0 additions & 44 deletions
This file was deleted.

FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Services/AppleProviderAuthUI.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ extension AuthenticateWithAppleDialog: ASAuthorizationControllerDelegate {
114114

115115
// MARK: - Apple Provider Swift
116116

117-
public class AppleProviderSwift: AuthProviderSwift, DeleteUserSwift {
117+
public class AppleProviderSwift: AuthProviderSwift {
118118
public let scopes: [ASAuthorization.Scope]
119119
let providerId = "apple.com"
120120

@@ -139,7 +139,7 @@ public class AppleProviderSwift: AuthProviderSwift, DeleteUserSwift {
139139
}
140140

141141
public func deleteUser(user: User) async throws {
142-
let operation = AppleDeleteUserOperation(appleProvider: self)
142+
let operation = ProviderDeleteUserOperation(provider: self)
143143
try await operation(on: user)
144144
}
145145
}

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService+Email.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
@preconcurrency import FirebaseAuth
1616
import Observation
1717

18+
@MainActor
1819
protocol EmailPasswordOperationReauthentication {
1920
var passwordPrompt: PasswordPromptCoordinator { get }
2021
}
2122

2223
extension EmailPasswordOperationReauthentication {
23-
// TODO: - @MainActor because User is non-sendable. Might change this once User is sendable in firebase-ios-sdk
24-
@MainActor func reauthenticate() async throws {
24+
func reauthenticate() async throws {
2525
guard let user = Auth.auth().currentUser else {
2626
throw AuthServiceError.reauthenticationRequired("No user currently signed-in")
2727
}
@@ -34,7 +34,7 @@ extension EmailPasswordOperationReauthentication {
3434
let password = try await passwordPrompt.confirmPassword()
3535

3636
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
37-
try await Auth.auth().currentUser?.reauthenticate(with: credential)
37+
_ = try await Auth.auth().currentUser?.reauthenticate(with: credential)
3838
} catch {
3939
throw AuthServiceError.signInFailed(underlying: error)
4040
}

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService.swift

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public protocol AuthenticatedOperation {
3333

3434
public extension AuthenticatedOperation {
3535
func callAsFunction(on _: User,
36-
_ performOperation: () async throws -> Void) async throws {
36+
_ performOperation: @MainActor () async throws -> Void) async throws {
3737
do {
3838
try await performOperation()
3939
} catch let error as NSError where error.requiresReauthentication {
@@ -45,3 +45,41 @@ public extension AuthenticatedOperation {
4545
}
4646
}
4747
}
48+
49+
@MainActor
50+
public protocol ProviderOperationReauthentication {
51+
var authProvider: AuthProviderSwift { get }
52+
}
53+
54+
public extension ProviderOperationReauthentication {
55+
func reauthenticate() async throws {
56+
guard let user = Auth.auth().currentUser else {
57+
throw AuthServiceError.reauthenticationRequired("No user currently signed-in")
58+
}
59+
60+
do {
61+
let credential = try await authProvider.createAuthCredential()
62+
_ = try await user.reauthenticate(with: credential)
63+
} catch {
64+
throw AuthServiceError.signInFailed(underlying: error)
65+
}
66+
}
67+
}
68+
69+
@MainActor
70+
public class ProviderDeleteUserOperation<Provider: AuthProviderSwift>: AuthenticatedOperation,
71+
@preconcurrency ProviderOperationReauthentication {
72+
let provider: Provider
73+
74+
public var authProvider: AuthProviderSwift { provider }
75+
76+
public init(provider: Provider) {
77+
self.provider = provider
78+
}
79+
80+
public func callAsFunction(on user: User) async throws {
81+
try await callAsFunction(on: user) {
82+
try await user.delete()
83+
}
84+
}
85+
}

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import SwiftUI
1717

1818
public protocol AuthProviderSwift {
1919
@MainActor func createAuthCredential() async throws -> AuthCredential
20+
@MainActor func deleteUser(user: User) async throws
2021
}
2122

2223
public protocol AuthProviderUI {
@@ -25,10 +26,6 @@ public protocol AuthProviderUI {
2526
var provider: AuthProviderSwift { get }
2627
}
2728

28-
public protocol DeleteUserSwift {
29-
@MainActor func deleteUser(user: User) async throws
30-
}
31-
3229
public protocol PhoneAuthProviderSwift: AuthProviderSwift {
3330
@MainActor func verifyPhoneNumber(phoneNumber: String) async throws -> String
3431
}
@@ -282,13 +279,12 @@ public extension AuthService {
282279
let operation = EmailPasswordDeleteUserOperation(passwordPrompt: passwordPrompt)
283280
try await operation(on: user)
284281
} else {
285-
// Find provider by matching ID and ensure it can delete users
286-
guard let matchingProvider = providers.first(where: { $0.id == providerId }),
287-
let provider = matchingProvider.provider as? DeleteUserSwift else {
282+
// Find provider by matching ID
283+
guard let matchingProvider = providers.first(where: { $0.id == providerId }) else {
288284
throw AuthServiceError.providerNotFound("No provider found for \(providerId)")
289285
}
290286

291-
try await provider.deleteUser(user: user)
287+
try await matchingProvider.provider.deleteUser(user: user)
292288
}
293289
}
294290
} catch {
@@ -723,10 +719,10 @@ public extension AuthService {
723719
}
724720
let password = try await passwordPrompt.confirmPassword()
725721
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
726-
try await user.reauthenticate(with: credential)
722+
_ = try await user.reauthenticate(with: credential)
727723
} else if let matchingProvider = providers.first(where: { $0.id == providerId }) {
728724
let credential = try await matchingProvider.provider.createAuthCredential()
729-
try await user.reauthenticate(with: credential)
725+
_ = try await user.reauthenticate(with: credential)
730726
} else {
731727
throw AuthServiceError.providerNotFound("No provider found for \(providerId)")
732728
}

FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/AccountService+Facebook.swift

Lines changed: 0 additions & 58 deletions
This file was deleted.

FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderAuthUI.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import FirebaseAuth
1919
import FirebaseAuthSwiftUI
2020
import SwiftUI
2121

22-
public class FacebookProviderSwift: AuthProviderSwift, DeleteUserSwift {
22+
public class FacebookProviderSwift: AuthProviderSwift {
2323
let scopes: [String]
2424
let providerId = "facebook.com"
2525
private let loginManager = LoginManager()
@@ -118,7 +118,7 @@ public class FacebookProviderSwift: AuthProviderSwift, DeleteUserSwift {
118118
}
119119

120120
public func deleteUser(user: User) async throws {
121-
let operation = FacebookDeleteUserOperation(facebookProvider: self)
121+
let operation = ProviderDeleteUserOperation(provider: self)
122122
try await operation(on: user)
123123
}
124124
}

0 commit comments

Comments
 (0)