Skip to content

Commit 68a244d

Browse files
darinfcompnerd
authored andcommitted
Modernize implementation of FirebaseAuth methods that return a Future (#54)
Also provides `completion` variants of async methods to support compatibility with the Firebase Objective C API. Other changes: - Fixes return value of `signOut`. - Removes workaround for SR70253 as it is no longer reproducible / appears to have been resolved. - Wraps C++ `Auth` type in a class to provide reference semantics.
1 parent 26ffcf9 commit 68a244d

File tree

4 files changed

+177
-88
lines changed

4 files changed

+177
-88
lines changed

Sources/FirebaseAuth/AuthStateDidChangeListenerHandle.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal class _AuthStateDidChangeListenerHandle {
1919
self.listener = swift_firebase.swift_cxx_shims.firebase.auth.AuthStateListener.Create({ auth, user, callback in
2020
guard let auth else { return }
2121
if let callback, let body = Unmanaged<AnyObject>.fromOpaque(callback).takeUnretainedValue() as? ((Auth, User?) -> Void) {
22-
body(auth, user.pointee.is_valid() ? user.pointee : nil)
22+
body(.init(auth), user.pointee.is_valid() ? user.pointee : nil)
2323
}
2424
}, UnsafeMutableRawPointer(self.callback.toOpaque()))
2525
}

Sources/FirebaseAuth/FirebaseAuth+Swift.swift

Lines changed: 137 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,37 @@ import FirebaseCore
88
import CxxShim
99
import Foundation
1010

11-
public typealias Auth = UnsafeMutablePointer<firebase.auth.Auth>
12-
1311
@available(*, unavailable)
1412
public enum AuthAPNSTokenType: Int {
1513
case FIRAuthAPNSTokenTypeUnknown
1614
case FIRAuthAPNSTokenTypeSandbox
1715
case FIRAuthAPNSTokenTypeProd
1816
}
1917

20-
extension Auth {
18+
public final class Auth {
19+
let impl: UnsafeMutablePointer<firebase.auth.Auth>
20+
21+
init(_ impl: UnsafeMutablePointer<firebase.auth.Auth>) {
22+
self.impl = impl
23+
}
24+
2125
public var app: FirebaseApp? {
22-
self.pointee.__appUnsafe()
26+
impl.pointee.__appUnsafe()
2327
}
2428

2529
public var currentUser: User? {
26-
let user = self.pointee.current_user()
30+
let user = impl.pointee.current_user()
2731
guard user.is_valid() else { return nil }
2832
return user
2933
}
3034

3135
public var languageCode: String? {
3236
get {
33-
let code = String(self.pointee.language_code())
37+
let code = String(impl.pointee.language_code())
3438
guard !code.isEmpty else { return nil }
3539
return String(code)
3640
}
37-
set { self.pointee.set_language_code(newValue) }
41+
set { impl.pointee.set_language_code(newValue) }
3842
}
3943

4044
// @available(*, unavailable)
@@ -61,67 +65,91 @@ extension Auth {
6165
}
6266

6367
public static func auth(app: FirebaseApp) -> Auth {
64-
firebase.auth.Auth.GetAuth(app, nil)
68+
.init(firebase.auth.Auth.GetAuth(app, nil))
69+
}
70+
71+
public func updateCurrentUser(_ user: User, completion: ((Error?) -> Void)?) {
72+
fatalError("\(#function) not yet implemented")
6573
}
6674

6775
public func updateCurrentUser(_ user: User) async throws {
6876
fatalError("\(#function) not yet implemented")
6977
}
7078

79+
public func fetchSignInMethods(forEmail email: String, completion: (([String]?, Error?) -> Void)?) {
80+
fetchSignInMethodsImpl(forEmail: email) { providers, error in
81+
if let completion {
82+
DispatchQueue.main.async {
83+
completion(providers, error)
84+
}
85+
}
86+
}
87+
}
88+
7189
public func fetchSignInMethods(forEmail email: String) async throws
7290
-> [String] {
73-
typealias Promise = CheckedContinuation<UnsafeMutablePointer<firebase.auth.Auth.FetchProvidersResult>, any Error>
74-
let providers = try await withCheckedThrowingContinuation { (continuation: Promise) in
75-
let future = self.pointee.FetchProvidersForEmail(email)
76-
withUnsafePointer(to: continuation) { continuation in
77-
future.OnCompletion_SwiftWorkaround({ future, pvContinuation in
78-
let pContinuation = pvContinuation?.assumingMemoryBound(to: Promise.self)
79-
if future.pointee.error() == 0 {
80-
pContinuation.pointee.resume(returning: .init(mutating: future.pointee.__resultUnsafe()))
81-
} else {
82-
let code = future.pointee.error()
83-
let message = String(cString: future.pointee.__error_messageUnsafe()!)
84-
pContinuation.pointee.resume(throwing: FirebaseError(code: code, message: message))
85-
}
86-
}, UnsafeMutableRawPointer(mutating: continuation))
91+
try await withCheckedThrowingContinuation { continuation in
92+
fetchSignInMethodsImpl(forEmail: email) { providers, error in
93+
if let error {
94+
continuation.resume(throwing: error)
95+
} else {
96+
continuation.resume(returning: providers ?? [])
97+
}
8798
}
88-
future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
8999
}
100+
}
90101

91-
#if SR70253
92-
// Workaround compiler crash (SR70253) by not using compactMap
93-
// which uses std.vector<std.string>::const_iterator.
94-
var result: [String] = []
95-
for idx in 0..<providers.pointee.providers.size() {
96-
result.append(String(providers.pointee.providers[idx]))
102+
private func fetchSignInMethodsImpl(forEmail email: String, completion: @escaping ([String]?, Error?) -> Void) {
103+
let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_fetch_providers_for_email(impl, email)
104+
future.setCompletion({
105+
let (result, error) = future.resultAndError
106+
var providers: [String]?
107+
if let result {
108+
providers = result.providers.map(String.init)
109+
} else {
110+
providers = nil
111+
}
112+
completion(providers, error)
113+
})
114+
}
115+
116+
public func signIn(withEmail email: String, password: String, completion: ((AuthDataResult?, Error?) -> Void)?) {
117+
signInImpl(withEmail: email, password: password) { data, error in
118+
if let completion {
119+
DispatchQueue.main.async {
120+
completion(data, error)
121+
}
122+
}
97123
}
98-
return result
99-
#else
100-
return providers.pointee.providers.compactMap(String.init)
101-
#endif
102124
}
103125

104126
public func signIn(withEmail email: String, password: String) async throws
105127
-> AuthDataResult {
106-
typealias Promise = CheckedContinuation<AuthDataResult, any Error>
107-
return try await withCheckedThrowingContinuation { (continuation: Promise) in
108-
let future = self.pointee.SignInWithEmailAndPassword(email, password)
109-
withUnsafePointer(to: continuation) { continuation in
110-
future.OnCompletion_SwiftWorkaround({ future, pvContinuation in
111-
let pContinuation = pvContinuation?.assumingMemoryBound(to: Promise.self)
112-
if future.pointee.error() == 0 {
113-
pContinuation.pointee.resume(returning: .init(mutating: future.pointee.__resultUnsafe()))
114-
} else {
115-
let code = future.pointee.error()
116-
let message = String(cString: future.pointee.__error_messageUnsafe()!)
117-
pContinuation.pointee.resume(throwing: FirebaseError(code: code, message: message))
118-
}
119-
}, UnsafeMutableRawPointer(mutating: continuation))
128+
try await withCheckedThrowingContinuation { continuation in
129+
signInImpl(withEmail: email, password: password) { data, error in
130+
if let error {
131+
continuation.resume(throwing: error)
132+
} else {
133+
continuation.resume(returning: data ?? .init())
134+
}
120135
}
121-
future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
122136
}
123137
}
124138

139+
private func signInImpl(withEmail email: String, password: String, completion: @escaping (AuthDataResult?, Error?) -> Void) {
140+
let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_sign_in_with_email_and_password(impl, email, password)
141+
future.setCompletion({
142+
let (result, error) = future.resultAndError
143+
var data: AuthDataResult?
144+
if let result {
145+
data = .init(result)
146+
} else {
147+
data = nil
148+
}
149+
completion(data, error)
150+
})
151+
}
152+
125153
public func signIn(withEmail email: String, link: String) async throws
126154
-> AuthDataResult {
127155
fatalError("\(#function) not yet implemented")
@@ -143,27 +171,43 @@ extension Auth {
143171
fatalError("\(#function) not yet implemented")
144172
}
145173

174+
public func createUser(withEmail email: String, password: String, completion: ((AuthDataResult?, Error?) -> Void)?) {
175+
createUserImpl(withEmail: email, password: password) { data, error in
176+
if let completion {
177+
DispatchQueue.main.async {
178+
completion(data, error)
179+
}
180+
}
181+
}
182+
}
183+
146184
public func createUser(withEmail email: String, password: String) async throws
147185
-> AuthDataResult {
148-
typealias Promise = CheckedContinuation<AuthDataResult, any Error>
149-
return try await withCheckedThrowingContinuation { (continuation: Promise) in
150-
let future = self.pointee.CreateUserWithEmailAndPassword(email, password)
151-
withUnsafePointer(to: continuation) { continuation in
152-
future.OnCompletion_SwiftWorkaround({ future, pvContinuation in
153-
let pContinuation = pvContinuation?.assumingMemoryBound(to: Promise.self)
154-
if future.pointee.error() == 0 {
155-
pContinuation.pointee.resume(returning: .init(mutating: future.pointee.__resultUnsafe()))
156-
} else {
157-
let code = future.pointee.error()
158-
let message = String(cString: future.pointee.__error_messageUnsafe()!)
159-
pContinuation.pointee.resume(throwing: FirebaseError(code: code, message: message))
160-
}
161-
}, UnsafeMutableRawPointer(mutating: continuation))
186+
try await withCheckedThrowingContinuation { continuation in
187+
createUserImpl(withEmail: email, password: password) { data, error in
188+
if let error {
189+
continuation.resume(throwing: error)
190+
} else {
191+
continuation.resume(returning: data ?? .init())
192+
}
162193
}
163-
future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
164194
}
165195
}
166196

197+
private func createUserImpl(withEmail email: String, password: String, completion: @escaping (AuthDataResult?, Error?) -> Void) {
198+
let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_create_user_with_email_and_password(impl, email, password)
199+
future.setCompletion({
200+
let (result, error) = future.resultAndError
201+
var data: AuthDataResult?
202+
if let result {
203+
data = .init(result)
204+
} else {
205+
data = nil
206+
}
207+
completion(data, error)
208+
})
209+
}
210+
167211
public func confirmPasswordReset(withCode code: String,
168212
newPassword: String) async throws {
169213
fatalError("\(#function) not yet implemented")
@@ -181,26 +225,36 @@ extension Auth {
181225
fatalError("\(#function) not yet implemented")
182226
}
183227

228+
public func sendPasswordReset(withEmail email: String, completion: ((Error?) -> Void)?) {
229+
sendPasswordResetImpl(withEmail: email) { error in
230+
if let completion {
231+
DispatchQueue.main.async {
232+
completion(error)
233+
}
234+
}
235+
}
236+
}
237+
184238
public func sendPasswordReset(withEmail email: String) async throws {
185-
typealias Promise = CheckedContinuation<Void, any Error>
186-
return try await withCheckedThrowingContinuation { (continuation: Promise) in
187-
let future = self.pointee.SendPasswordResetEmail(email)
188-
withUnsafePointer(to: continuation) { continuation in
189-
future.OnCompletion_SwiftWorkaround({ future, pvContinuation in
190-
let pContinuation = pvContinuation?.assumingMemoryBound(to: Promise.self)
191-
if future.pointee.error() == 0 {
192-
pContinuation.pointee.resume()
193-
} else {
194-
let code = future.pointee.error()
195-
let message = String(cString: future.pointee.__error_messageUnsafe()!)
196-
pContinuation.pointee.resume(throwing: FirebaseError(code: code, message: message))
197-
}
198-
}, UnsafeMutableRawPointer(mutating: continuation))
239+
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, any Error>) in
240+
sendPasswordResetImpl(withEmail: email) { error in
241+
if let error {
242+
continuation.resume(throwing: error)
243+
} else {
244+
continuation.resume()
245+
}
199246
}
200-
future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
201247
}
202248
}
203249

250+
private func sendPasswordResetImpl(withEmail email: String, completion: @escaping (Error?) -> Void) {
251+
let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_send_password_reset_email(impl, email)
252+
future.setCompletion({
253+
let (_, error) = future.resultAndError
254+
completion(error)
255+
})
256+
}
257+
204258
// public func sendPasswordReset(withEmail email: String,
205259
// actionCodeSettings: ActionCodeSettings) async throws {
206260
// fatalError("\(#function) not yet implemented")
@@ -211,8 +265,9 @@ extension Auth {
211265
// fatalError("\(#function) not yet implemented")
212266
// }
213267

214-
public func signOut() throws {
215-
self.pointee.SignOut()
268+
@discardableResult public func signOut() throws -> Bool {
269+
impl.pointee.SignOut()
270+
return true
216271
}
217272

218273
public func isSignIn(withEmailLink link: String) -> Bool {
@@ -222,13 +277,13 @@ extension Auth {
222277
public func addStateDidChangeListener(_ listener: @escaping (Auth, User?) -> Void)
223278
-> AuthStateDidChangeListenerHandle {
224279
let handle = _AuthStateDidChangeListenerHandle(listener)
225-
self.pointee.AddAuthStateListener(handle.listener)
280+
impl.pointee.AddAuthStateListener(handle.listener)
226281
return handle
227282
}
228283

229284
public func removeStateDidChangeListener(_ listenerHandle: AuthStateDidChangeListenerHandle) {
230285
guard let handle = listenerHandle as? _AuthStateDidChangeListenerHandle else { return }
231-
self.pointee.RemoveAuthStateListener(handle.listener)
286+
impl.pointee.RemoveAuthStateListener(handle.listener)
232287
}
233288

234289
// public func addIDTokenDidChangeListener(_ listener: @escaping (Auth, User?) -> Void)

Sources/FirebaseAuth/FirebaseAuthResult+Swift.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@
33
@_exported
44
import firebase
55

6-
public typealias AuthDataResult = UnsafeMutablePointer<firebase.auth.AuthResult>
6+
//public typealias AuthDataResult = UnsafeMutablePointer<firebase.auth.AuthResult>
7+
8+
public final class AuthDataResult {
9+
let impl: firebase.auth.AuthResult
10+
11+
init(_ impl: firebase.auth.AuthResult = .init()) {
12+
self.impl = impl
13+
}
714

8-
extension AuthDataResult {
915
public var user: User? {
10-
let user = self.pointee.user
11-
guard user.is_valid() else { return nil }
12-
return user
16+
guard impl.user.is_valid() else { return nil }
17+
return impl.user
1318
}
1419

1520
// public var additionalUserInfo: AdditionalUserInfo? {

Sources/firebase/include/FirebaseAuth.hh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,34 @@ user_send_email_verification(::firebase::auth::User user) {
7171
user.SendEmailVerification());
7272
}
7373

74+
inline ::swift_firebase::swift_cxx_shims::firebase::Future<
75+
::firebase::auth::Auth::FetchProvidersResult>
76+
auth_fetch_providers_for_email(
77+
::firebase::auth::Auth* auth, const char* email) {
78+
return auth->FetchProvidersForEmail(email);
79+
}
80+
81+
inline ::swift_firebase::swift_cxx_shims::firebase::Future<
82+
::firebase::auth::AuthResult>
83+
auth_sign_in_with_email_and_password(
84+
::firebase::auth::Auth* auth, const char* email, const char* password) {
85+
return auth->SignInWithEmailAndPassword(email, password);
86+
}
87+
88+
inline ::swift_firebase::swift_cxx_shims::firebase::Future<
89+
::firebase::auth::AuthResult>
90+
auth_create_user_with_email_and_password(
91+
::firebase::auth::Auth* auth, const char* email, const char* password) {
92+
return auth->CreateUserWithEmailAndPassword(email, password);
93+
}
94+
95+
inline ::swift_firebase::swift_cxx_shims::firebase::VoidFuture
96+
auth_send_password_reset_email(
97+
::firebase::auth::Auth* auth, const char* email) {
98+
return ::swift_firebase::swift_cxx_shims::firebase::VoidFuture::From(
99+
auth->SendPasswordResetEmail(email));
100+
}
101+
74102
class SWIFT_UNSAFE_REFERENCE AuthStateListener
75103
: public ::firebase::auth::AuthStateListener {
76104
typedef void (*Handler)(::firebase::auth::Auth *auth,
@@ -108,6 +136,7 @@ private:
108136
Handler block_;
109137
void *user_data_;
110138
};
139+
111140
} // namespace swift_firebase::swift_cxx_shims::firebase::auth
112141

113142
#endif

0 commit comments

Comments
 (0)