Skip to content
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

[WPP-1581] Modernize implementation of methods that return a Future #44

Merged
merged 3 commits into from
Mar 12, 2024
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
172 changes: 110 additions & 62 deletions Sources/FirebaseAuth/FirebaseUser+Swift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import firebase
@_spi(FirebaseInternal)
import FirebaseCore

import CxxShim

public typealias User = firebase.auth.User
public typealias AuthResult = firebase.auth.AuthResult

Expand Down Expand Up @@ -66,45 +68,64 @@ extension User {
// fatalError("\(#function) not yet implemented")
// }

public mutating func reload(completion: ((Error?) -> Void)?) {
reloadImpl() { error in
if let completion {
DispatchQueue.main.async {
completion(error)
}
}
}
}

public mutating func reload() async throws {
typealias Promise = CheckedContinuation<Void, any Error>
try await withCheckedThrowingContinuation { (continuation: Promise) in
let future = self.Reload()
withUnsafePointer(to: continuation) { continuation in
future.OnCompletion_SwiftWorkaround({ future, pvContinuation in
let pContinuation = pvContinuation?.assumingMemoryBound(to: Promise.self)
if future.pointee.error() == 0 {
pContinuation.pointee.resume()
} else {
let code = future.pointee.error()
let message = String(cString: future.pointee.__error_messageUnsafe()!)
pContinuation.pointee.resume(throwing: FirebaseError(code: code, message: message))
}
}, UnsafeMutableRawPointer(mutating: continuation))
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, any Error>) in
reloadImpl() { error in
if let error {
continuation.resume(throwing: error)
} else {
continuation.resume()
}
}
future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
}
}

public mutating func reauthenticate(with credential: Credential) async throws
-> AuthResult {
typealias Promise = CheckedContinuation<firebase.auth.AuthResult, any Error>
return try await withCheckedThrowingContinuation { (continuation: Promise) in
let future = self.ReauthenticateAndRetrieveData(credential)
withUnsafePointer(to: continuation) { continuation in
future.OnCompletion_SwiftWorkaround({ future, pvContinuation in
let pContinuation = pvContinuation?.assumingMemoryBound(to: Promise.self)
if future.pointee.error() == 0 {
pContinuation.pointee.resume(returning: future.pointee.__resultUnsafe().pointee)
} else {
let code = future.pointee.error()
let message = String(cString: future.pointee.__error_messageUnsafe()!)
pContinuation.pointee.resume(throwing: FirebaseError(code: code, message: message))
}
}, UnsafeMutableRawPointer(mutating: continuation))
private mutating func reloadImpl(completion: @escaping (Error?) -> Void) {
let future = swift_firebase.swift_cxx_shims.firebase.auth.user_reload(self)
future.setCompletion({
let (_, error) = future.resultAndError
completion(error)
})
}

public mutating func reauthenticate(with credential: Credential, completion: ((AuthResult?, Error?) -> Void)?) {
reauthenticateImpl(with: credential) { result, error in
if let completion {
DispatchQueue.main.async {
completion(result, error)
}
future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
}
}
}

public mutating func reauthenticate(with credential: Credential) async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, any Error>) in
reauthenticateImpl(with: credential) { result, error in
if let error {
continuation.resume(throwing: error)
} else {
continuation.resume()
}
}
}
}

public mutating func reauthenticateImpl(with credential: Credential, completion: @escaping (AuthResult?, Error?) -> Void) {
let future = swift_firebase.swift_cxx_shims.firebase.auth.user_reauthenticate_and_retrieve_data(self, credential)
future.setCompletion({
let (result, error) = future.resultAndError
completion(result, error)
})
}

// -reauthenticateWithProvider:UIDelegate:completion:
Expand All @@ -122,27 +143,43 @@ extension User {
return try await idTokenForcingRefresh(false)
}

public mutating func idTokenForcingRefresh(_ forceRefresh: Bool, completion: ((String?, Error?) -> Void)?) {
idTokenForcingRefreshImpl(forceRefresh) { result, error in
if let completion {
DispatchQueue.main.async {
completion(result, error)
}
}
}
}

public mutating func idTokenForcingRefresh(_ forceRefresh: Bool) async throws
-> String {
typealias Promise = CheckedContinuation<String, any Error>
return try await withCheckedThrowingContinuation { (continuation: Promise) in
let future = self.GetToken(forceRefresh)
withUnsafePointer(to: continuation) { continuation in
future.OnCompletion_SwiftWorkaround({ future, pvContinuation in
let pContinuation = pvContinuation?.assumingMemoryBound(to: Promise.self)
if future.pointee.error() == 0 {
pContinuation.pointee.resume(returning: String(future.pointee.__resultUnsafe().pointee))
} else {
let code = future.pointee.error()
let message = String(cString: future.pointee.__error_messageUnsafe()!)
pContinuation.pointee.resume(throwing: FirebaseError(code: code, message: message))
}
}, UnsafeMutableRawPointer(mutating: continuation))
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<String, any Error>) in
idTokenForcingRefreshImpl(forceRefresh) { result, error in
if let error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: result ?? .init())
}
}
future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
}
}

private mutating func idTokenForcingRefreshImpl(_ forceRefresh: Bool, completion: @escaping (String?, Error?) -> Void) {
let future = swift_firebase.swift_cxx_shims.firebase.auth.user_get_token(self, forceRefresh)
future.setCompletion({
let (result, error) = future.resultAndError
let stringResult: String?
if let result {
stringResult = String(result)
} else {
stringResult = nil
}
completion(stringResult, error)
})
}

// public func link(with credential: AuthCredential) async throws
// -> AuthDataResult {
// fatalError("\(#function) not yet implemented")
Expand All @@ -154,26 +191,37 @@ extension User {
fatalError("\(#function) not yet implemented")
}

public mutating func sendEmailVerification(completion: ((Error?) -> Void)?) {
sendEmailVerificationImpl() { error in
if let completion {
DispatchQueue.main.async {
completion(error)
}
}
}
}

public mutating func sendEmailVerification() async throws {
typealias Promise = CheckedContinuation<Void, any Error>
try await withCheckedThrowingContinuation { (continuation: Promise) in
let future = self.SendEmailVerification()
withUnsafePointer(to: continuation) { continuation in
future.OnCompletion_SwiftWorkaround({ future, pvContinuation in
let pContinuation = pvContinuation?.assumingMemoryBound(to: Promise.self)
if future.pointee.error() == 0 {
pContinuation.pointee.resume()
} else {
let code = future.pointee.error()
let message = String(cString: future.pointee.__error_messageUnsafe()!)
pContinuation.pointee.resume(throwing: FirebaseError(code: code, message: message))
}
}, UnsafeMutableRawPointer(mutating: continuation))
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, any Error>) in
sendEmailVerificationImpl() { error in
if let error {
continuation.resume(throwing: error)
} else {
continuation.resume()
}
}
future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
}
}

public mutating func sendEmailVerificationImpl(completion: @escaping (Error?) -> Void) {
//let future = self.SendEmailVerification()
let future = swift_firebase.swift_cxx_shims.firebase.auth.user_send_email_verification(self)
future.setCompletion({
let (_, error) = future.resultAndError
completion(error)
})
}

// public func sendEmailVerification(with actionCodeSettings: ActionCodeSettings) async throws {
// fatalError("\(#function) not yet implemented")
// }
Expand Down
2 changes: 1 addition & 1 deletion Sources/FirebaseCore/FutureProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public extension FutureProtocol {
}

var result: ResultType? {
__resultUnsafe().pointee
__resultUnsafe()?.pointee
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the crash fix.

}

var errorMessage: String? {
Expand Down
66 changes: 23 additions & 43 deletions Sources/FirebaseFirestore/DocumentReference+Swift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ import Foundation
public typealias DocumentReference = firebase.firestore.DocumentReference

extension DocumentReference {
// Use a serial dispatch queue to write mutations from the block-based API.
// Passing these futures into the queue as an item should retain them long enough
// to do their job, but not block.
private static let mutationQueue = DispatchQueue(label: "firebase.firestore.document.mutations")

public var documentID: String {
String(swift_firebase.swift_cxx_shims.firebase.firestore.document_id(self))
}
Expand Down Expand Up @@ -87,51 +82,36 @@ extension DocumentReference {
return ListenerRegistration(boxed, instance)
}

public func setData(_ data: [String: Any], merge: Bool = false, completion: ((NSError?) -> Void)?) {
let boxed = Unmanaged.passRetained(completion as AnyObject)
let converted = FirestoreDataConverter.firestoreValue(document: data)
let options = merge ? firebase.firestore.SetOptions.Merge() : firebase.firestore.SetOptions()

Self.mutationQueue.async {
let future = swift_firebase.swift_cxx_shims.firebase.firestore.document_set_data(self, converted, options)

future.OnCompletion_SwiftWorkaround({ future, pvCallback in
guard let pCallback = pvCallback, let callback = Unmanaged<AnyObject>.fromOpaque(pCallback).takeUnretainedValue() as? ((NSError?) -> Void)? else {
return
}
if let code = future?.pointee.error(), code != 0 {
callback?(NSError(domain: "firebase.firestore.document", code: Int(code)))
} else {
callback?(nil)
public func setData(_ data: [String: Any], merge: Bool = false, completion: ((Error?) -> Void)?) {
setDataImpl(data, merge: merge) { error in
if let completion {
DispatchQueue.main.async {
completion(error)
}
}, UnsafeMutableRawPointer(boxed.toOpaque()))

future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
}
}
}
}

extension DocumentReference {
public func setData(_ data: [String: Any], merge: Bool = false) async throws {
private func setDataImpl(_ data: [String: Any], merge: Bool, completion: @escaping (Error?) -> Void) {
let converted = FirestoreDataConverter.firestoreValue(document: data)
let options = merge ? firebase.firestore.SetOptions.Merge() : firebase.firestore.SetOptions()
let future = swift_firebase.swift_cxx_shims.firebase.firestore.document_set_data(self, converted, options)
future.setCompletion({
let (_, error) = future.resultAndError
completion(error)
})
}
}

typealias Promise = CheckedContinuation<Void, any Error>
try await withCheckedThrowingContinuation { (continuation: Promise) in
let future = swift_firebase.swift_cxx_shims.firebase.firestore.document_set_data(self, converted, options)
withUnsafePointer(to: continuation) { continuation in
future.OnCompletion_SwiftWorkaround({ future, pvContinuation in
let pContinuation = pvContinuation?.assumingMemoryBound(to: Promise.self)
if future.pointee.error() == 0 {
pContinuation.pointee.resume()
} else {
let code = future.pointee.error()
let message = String(cString: future.pointee.__error_messageUnsafe()!)
pContinuation.pointee.resume(throwing: FirebaseError(code: code, message: message))
}
}, UnsafeMutableRawPointer(mutating: continuation))

future.Wait(firebase.FutureBase.kWaitTimeoutInfinite)
extension DocumentReference {
public func setData(_ data: [String: Any], merge: Bool = false) async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, any Error>) in
setDataImpl(data, merge: merge) { error in
if let error {
continuation.resume(throwing: error)
} else {
continuation.resume()
}
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions Sources/firebase/include/FirebaseAuth.hh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#include <firebase/auth.h>

#include "FirebaseCore.hh"

// https://github.com/apple/swift/issues/69959
#if __has_include(<swift/bridging>)
#include <swift/bridging>
Expand All @@ -16,6 +18,7 @@
#endif

namespace swift_firebase::swift_cxx_shims::firebase::auth {

inline std::string
user_display_name(const ::firebase::auth::User &user) noexcept {
return user.display_name();
Expand Down Expand Up @@ -43,6 +46,31 @@ inline std::string user_uid(const ::firebase::auth::User &user) noexcept {
return user.uid();
}

inline ::swift_firebase::swift_cxx_shims::firebase::VoidFuture
user_reload(::firebase::auth::User user) {
return ::swift_firebase::swift_cxx_shims::firebase::VoidFuture::From(
user.Reload());
}

inline ::swift_firebase::swift_cxx_shims::firebase::Future<
::firebase::auth::AuthResult>
user_reauthenticate_and_retrieve_data(
::firebase::auth::User user,
const ::firebase::auth::Credential& credential) {
return user.ReauthenticateAndRetrieveData(credential);
}

inline ::swift_firebase::swift_cxx_shims::firebase::Future<::std::string>
user_get_token(::firebase::auth::User user, bool force_refresh) {
return user.GetToken(force_refresh);
}

inline ::swift_firebase::swift_cxx_shims::firebase::VoidFuture
user_send_email_verification(::firebase::auth::User user) {
return ::swift_firebase::swift_cxx_shims::firebase::VoidFuture::From(
user.SendEmailVerification());
}

class SWIFT_UNSAFE_REFERENCE AuthStateListener
: public ::firebase::auth::AuthStateListener {
typedef void (*Handler)(::firebase::auth::Auth *auth,
Expand Down
Loading
Loading