How could I return a Bool from this sign in with apple function that uses the authorizationController? I have been looking at withCheckedThrowingContinuation but with no luck. I have also seen some examples of using completion handlers but I would like to use async.
func signInWithApple(withState state: SignInState) async -> Bool {
let request = signInWithAppleRequest(withState: state)
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.performRequests()
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
//...
let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)
switch state {
case .signIn:
Task {
authenticationState = .authenticating
do {
let result = try await Auth.auth().signIn(with: credential)
if result.additionalUserInfo?.isNewUser == true {
self.isNewUser = true
}
return true
} catch {
self.authenticationState = .unauthenticated
print(error.localizedDescription)
return false
}
}
case .reauth:
Task {
if let user = Auth.auth().currentUser {
do {
try await user.reauthenticate(with: credential)
return true
} catch {
print(error.localizedDescription)
return false
}
}
}
}
}
}
I don't have all your types, so here is a simple example of a continuation which at least compiles.
The class must inherit from NSObject and adopt ASAuthorizationControllerDelegate.
The continuation is set in the same scope where the request is executed.
The most important rule is: The continuation must be resumed exactly once
import AuthenticationServices
class Authorization: NSObject, ASAuthorizationControllerDelegate {
private var continuation : CheckedContinuation<ASAuthorization,Error>?
func signInWithApple() async throws -> ASAuthorization {
return try await withCheckedThrowingContinuation { continuation in
self.continuation = continuation
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.performRequests()
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
continuation?.resume(returning: authorization)
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
continuation?.resume(throwing: error)
}
}
Related
I am working on SwiftUI and using Resolver for Dependency Injection. As a backend I am using Firebase. I've created an AuthSession file that handles all of my user authentication stuff. In the project I also have a number of other repositories that populate data throughout the app. In AuthSession I am creating properties for each repository so that I can start and stop Firestore Listeners on login and logout. In a couple of these repositories I want to access AuthSession through #InjectedObject so that when a user logs in I can be notified and can get updates via Combine. My issues is that when I start the app, it crashes with an odd Firebase error.
AuthSession.swift
class AuthSession: ObservableObject {
let db = Firestore.firestore()
var offerRepository: OfferRepository = Resolver.resolve()
var handle: AuthStateDidChangeListenerHandle?
#Published var currentUser: User?
#Published var loggedIn = false
#Published var currentUserUid = ""
// Combine Cancellable
private var cancellables = Set<AnyCancellable>()
// Intitalizer
init() {
}
func listen() {
print("AuthSession - listen called")
// Monitor Authentication chagnes using Firebase Auth.
handle = Auth.auth().addStateDidChangeListener{ (auth, user) in
// Check to see if a user is returned from a sign in or sign up event.
if let user = user {
// Set loggedIn to true. This will also be set when a new User is created in SignUpView.
print("User Exists.")
self.loggedIn = true
self.currentUserUid = user.uid
self.currentUser = user
} else {
print("Not logged in")
}
}
}
}
Below is OfferRepository. When the line below is added it crashes. If the line is removed it does not crash. I'm not sure why. The Combine code is not included.
Line causing the crash.
#InjectedObject var authSession: AuthSession
OfferRepository.swift
class OfferRepository: ObservableObject {
let db = Firestore.firestore()
private var snapshotListener: ListenerRegistration?
#InjectedObject var authSession: AuthSession
#Published var offers = [Offer]()
private var cancellables = Set<AnyCancellable>()
init() {
startSnapshotListener()
}
func startSnapshotListener() {
if snapshotListener == nil {
self.snapshotListener = db.collection(FirestoreCollection.offers).order(by: "created", descending: true).addSnapshotListener { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error)")
} else {
guard let documents = querySnapshot?.documents else {
print("No Offers.")
return
}
self.offers = documents.compactMap { offer in
do {
return try offer.data(as: Offer.self)
} catch {
print(error)
}
return nil
}
}
}
}
}
}
For reference here is my AppDelegate+Registering file.
extension Resolver: ResolverRegistering {
public static func registerAllServices() {
register { AuthSession() }.scope(.application)
register { OfferRepository() as OfferRepository }.scope(.application)
}
}
The app crashed on the line below from the Firestore package.
- (NSString *)keyForDatabase:(NSString *)database {
return [NSString stringWithFormat:#"%#|%#", self.app.name, database];
}
Thread 1: EXC_BAD_ACCESS (code=2, address=0x16d317ff8)
While I can start and stop listeners from login and logout views, I'd prefer to keep this in the AuthSession file. Is there a way around this?
#InjectedObject is intended to be used to inject ObservableObjects into SwiftUI views - see the docs: https://github.com/hmlongco/Resolver#property-wrappers
As you want to reference the AuthenticationService inside your repositories (which are ObservableObjects, you should use #Injected instead.
Here is a snippet from one of my apps:
public class ArtifactRepository: ObservableObject {
// MARK: - Dependencies
#Injected var db: Firestore
#Injected var authenticationService: AuthenticationService
// MARK: - Publishers
#Published public var artifacts = [Artifact]()
// MARK: - Private attributes
private var statusFilter: Status
private var userId: String = "unknown"
private var listenerRegistration: ListenerRegistration?
private var cancellables = Set<AnyCancellable>()
let logger = Logger(subsystem: "dev.peterfriese.App", category: "persistence")
public init(statusFilter: Status = .inbox, liveSync: Bool = true) {
// filtering
self.statusFilter = statusFilter
// observe user ID
authenticationService.$user
.compactMap { user in
user?.uid
}
.assign(to: \.userId, on: self)
.store(in: &cancellables)
// if live sync is on, (re)load data when user changes
if liveSync {
authenticationService.$user
.receive(on: DispatchQueue.main)
.sink { [weak self] user in
if self?.listenerRegistration != nil {
self?.unsubscribe()
self?.subscribe()
}
}
.store(in: &cancellables)
}
}
deinit {
unsubscribe()
}
public func unsubscribe() {
if listenerRegistration != nil {
listenerRegistration?.remove()
listenerRegistration = nil
}
}
public func subscribe() {
if listenerRegistration == nil {
var query = db.collection("artifacts")
.whereField("userId", isEqualTo: self.userId)
if (statusFilter != .all) {
query = query.whereField("status", isEqualTo: statusFilter.rawValue)
}
listenerRegistration = query.order(by: "dateAdded", descending: true)
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
self?.logger.debug("No documents")
return
}
self?.logger.debug("Mapping \(documents.count) documents")
self?.artifacts = documents.compactMap { queryDocumentSnapshot in
try? queryDocumentSnapshot.data(as: Artifact.self)
}
}
}
}
}
public class AuthenticationService: ObservableObject {
private let logger = Logger(subsystem: "dev.peterfriese.App", category: "authentication")
#Published public var user: User?
private var handle: AuthStateDidChangeListenerHandle?
public init() {
setupKeychainSharing()
registerStateListener()
}
public func signIn() {
if Auth.auth().currentUser == nil {
Auth.auth().signInAnonymously()
}
}
public func signOut() {
do {
try Auth.auth().signOut()
}
catch {
print("error when trying to sign out: \(error.localizedDescription)")
}
}
private let accessGroup = "XXXXXXX.dev.peterfriese.App"
private func setupKeychainSharing() {
do {
let auth = Auth.auth()
auth.shareAuthStateAcrossDevices = true
try auth.useUserAccessGroup(accessGroup)
}
catch let error as NSError {
print("Error changing user access group: %#", error)
}
}
private func registerStateListener() {
if handle == nil {
handle = Auth.auth().addStateDidChangeListener({ (auth, user) in
self.user = user
if let user = user {
if user.isAnonymous {
self.logger.debug("User signed in anonymously with user ID \(user.uid).")
}
else {
self.logger.debug("User signed in with user ID \(user.uid). Email: \(user.email ?? "(empty)"), display name: [\(user.displayName ?? "(empty)")]")
}
}
else {
self.logger.debug("User signed out.")
self.signIn()
}
})
}
}
}
UPDATED: I have a project that is using Firebase Firestore. I have snapshot listeners set up to my model objects. I start the snapshot listeners when the app starts. My understanding is that if you start a snapshot listener once that's all you need.
My issue is that when I create a new object in the app I must restart the snapshot listener in order for it to see the changes. I am wondering if I am making a mistake somewhere. Here are the files I think are important. If anything else is needed please let me know.
Main
Main where I create the environment objects for the project and start the UserRepository snapshot listener.
#main
struct TheExchangeApp: App {
// #EnvironmentObjects
#StateObject private var userRepository = UserRepository()
#StateObject private var authListener = AuthSession(userRepository: UserRepository())
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(userRepository)
.environmentObject(authListener)
}
}
}
UserRepository.swift
This is the user repository were I create the snapshot listener.
class UserRepository: ObservableObject {
// Access to Firestore Database
let db = Firestore.firestore()
private var snapshotListener: ListenerRegistraion?
#Published var users = [User]()
init() {
startSnapshotListener()
}
func startSnapshotListener() {
// Add a SnapshotListener to the User Collection.
if snapshotListner == nil {
db.collection("users").addSnapshotListener { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error)")
} else {
guard let documents = querySnapshot?.documents else {
print("No Users.")
return
}
self.users = documents.compactMap { user in
do {
return try user.data(as: User.self)
} catch {
print(error)
}
return nil
}
}
}
}
}
func stopSnapshotListener() {
if snapshotListener != nil {
snapshotListener?.remove()
snapshotListener = nil
}
}
func createNewUser(userUid: String, user: User) {
do {
let _ = try self.db.collection(FirestoreCollection.users).document(userUid).setData(from: user)
if snapshotListener == nil {
print("createNewUser snapshotListener is nil")
} else if snapshotListener != nil {
print("createNewUser snapshotListener is not nil")
}
} catch let error as NSError {
print("UserRepository - createNewUser Error: \(error)")
}
}
}
AuthSession
Here I start a combine publisher on the userRepository snapshot listener. The publisher is not getting an update when I create a new user in SignUpView below. If I restart the snapshot listener in UserRepository I get the published value.
class AuthSession: ObservableObject {
let db = Firestore.firestore()
var userRepository: UserRepository
#Published var currentUser: User?
private var cancellables = Set<AnyCancellable>()
init(userRepository: UserRepository) {
self.userRepository = userRepository
self.startCombine()
}
func startCombine() {
userRepository
.$users
.receive(on: RunLoop.main)
.map { users in
users
.first(where: { $0.id == self.currentUserUid})
}
.assign(to: \.currentUser, on: self)
.store(in: &cancellables)
}
}
SignUp View
Here is where I create a new user. The snapshot listener does not work unless I restart. I know because the combine publisher in AuthSession does not fire.
struct SignUpView: View {
#EnvironmentObject var authSession: AuthSession
let db = Firestore.firestore()
var body: some View {
Button(action: {
self.signUp()
}, label: {
Text("Sign Up")
})
}
func signUp() {
authSession.listen()
let newUser = User(email: myemail#email.com, name: "Hans")
self.authSession.userRepository.createNewUser(userUid: user.uid, user: newUser)
}
}
Any help here would be greatly appreciated.
I believe I've sorted out the issue. A developer I am working with changed the rules on Firestore. These new rules included the following for the User collection.
match /users/{userId} {
allow create: if request.auth.uid == userId;
allow read: if request.auth != null;
allow write: if request.auth.uid == userId;
Once I removed these rules everything works as expected. I hope this helps someone else out there.
In my project I'm using a trigger to create a user document in Firestore when the user signs in. And this is great - everything works perfect for Google Sign-In and Facebook Login.
Here is this trigger:
exports.createAccountDocument = functions.auth.user().onCreate(async user => {
const { uid, displayName } = user
const username = displayName;
const email = user.email || user.providerData[0].email;
const profileImageUrl = uid;
const status = "active";
const str = username;
const qwery = [];
for (let i = 0; i < str.length; ++i) {
qwery[i] = str.substring(0, i + 1).replace(/\s/g, "").toLowerCase();
}
const keywords = qwery;
const bio = "";
return await admin
.firestore()
.collection("users")
.doc(uid)
.set({ bio, email, keywords, profileImageUrl, status, uid, username })
})
For example - this is Facebook Login method:
import SwiftUI
import FBSDKLoginKit
import Firebase
struct FacebookAuthView: UIViewRepresentable {
#Binding var showAnimation: Bool
#Binding var showSheet: Bool
init(showAnimation: Binding<Bool>, showSheet: Binding<Bool>) {
self._showAnimation = showAnimation
self._showSheet = showSheet
}
func makeCoordinator() -> FacebookAuthView.Coordinator {
return FacebookAuthView.Coordinator(showAnimation: self.$showAnimation, showSheet: self.$showSheet)
}
class Coordinator: NSObject, LoginButtonDelegate {
#Binding var showAnimation: Bool
#Binding var showSheet: Bool
init(showAnimation: Binding<Bool>, showSheet: Binding<Bool>) {
self._showAnimation = showAnimation
self._showSheet = showSheet
}
func loginButton(_ loginButton: FBLoginButton, didCompleteWith result: LoginManagerLoginResult?, error: Error?) {
if let error = error {
print(error.localizedDescription)
return
}
guard let token = AccessToken.current else {
return
}
self.showAnimation = true
self.showSheet = false
let credential = FacebookAuthProvider.credential(withAccessToken: token.tokenString)
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error, (error as NSError).code == AuthErrorCode.credentialAlreadyInUse.rawValue {
Auth.auth().signIn(with: credential) { result, error in
// continue
print("signIn result: " + authResult!.user.email!)
if let token = firebaseRegistrationPushToken {
checkUserAuthSettings(pushToken: token)
}
}
} else {
// continue
print("Facebook Sign In")
if let token = firebaseRegistrationPushToken {
checkUserAuthSettings(pushToken: token)
}
}
}
}
func loginButtonDidLogOut(_ loginButton: FBLoginButton) {
try! Auth.auth().signOut()
}
}
func makeUIView(context: UIViewRepresentableContext<FacebookAuthView>) -> FBLoginButton {
let view = FBLoginButton()
view.permissions = ["email"]
view.delegate = context.coordinator
return view
}
func updateUIView(_ uiView: FBLoginButton, context: UIViewRepresentableContext<FacebookAuthView>) { }
}
But when I try to create a document when user logs in using Swign in with Apple, this doesn't work. In the Firebase console under Firebase Authentication I can see new the user, but in Firestore, no document shows up at all.
Here is my Sign in with Apple method:
import Foundation
import SwiftUI
import AuthenticationServices
import CryptoKit
import Firebase
struct AppleAuthView: UIViewRepresentable {
#Binding var showAnimation: Bool
#Binding var showSheet: Bool
init(showAnimation: Binding<Bool>, showSheet: Binding<Bool>) {
self._showAnimation = showAnimation
self._showSheet = showSheet
}
func makeCoordinator() -> AppleAuthView.Coordinator {
return AppleAuthView.Coordinator(showAnimation: self.$showAnimation, showSheet: self.$showSheet)
}
class Coordinator: NSObject, ASAuthorizationControllerPresentationContextProviding, ASAuthorizationControllerDelegate {
#Binding var showAnimation: Bool
#Binding var showSheet: Bool
fileprivate var currentNonce: String?
init(showAnimation: Binding<Bool>, showSheet: Binding<Bool>) {
self._showAnimation = showAnimation
self._showSheet = showSheet
super.init()
}
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
let viewController = UIApplication.shared.windows.last?.rootViewController
return (viewController?.view.window!)!
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
guard let nonce = currentNonce else {
fatalError("Invalid state: A login callback was received, but no login request was sent.")
}
guard let appleIDToken = appleIDCredential.identityToken else {
print("Unable to fetch identity token")
return
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
return
}
let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
print(error.localizedDescription)
return
}
print("Apple Sign In")
if let token = firebaseRegistrationPushToken {
checkUserAuthSettings(pushToken: token)
}
}
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
print("Sign in with Apple errored: \(error)")
}
#objc func startSignInWithAppleFlow() {
let nonce = randomNonceString()
currentNonce = nonce
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
request.nonce = sha256(nonce)
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
}
private func randomNonceString(length: Int = 32) -> String {
precondition(length > 0)
let charset: Array<Character> = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
var result = ""
var remainingLength = length
while remainingLength > 0 {
let randoms: [UInt8] = (0 ..< 16).map { _ in
var random: UInt8 = 0
let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
if errorCode != errSecSuccess {
fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)")
}
return random
}
randoms.forEach { random in
if remainingLength == 0 {
return
}
if random < charset.count {
result.append(charset[Int(random)])
remainingLength -= 1
}
}
}
return result
}
#available(iOS 13, *)
private func sha256(_ input: String) -> String {
let inputData = Data(input.utf8)
let hashedData = SHA256.hash(data: inputData)
let hashString = hashedData.compactMap {
return String(format: "%02x", $0)
}.joined()
return hashString
}
}
func makeUIView(context: Context) -> ASAuthorizationAppleIDButton {
let button = ASAuthorizationAppleIDButton(type: .signIn, style: .black)
button.addTarget(context.coordinator,action: #selector(Coordinator.startSignInWithAppleFlow),for: .touchUpInside)
return button
}
func updateUIView(_ uiView: ASAuthorizationAppleIDButton, context: Context) {
}
}
I don't understand why the document cannot be created. In the console I can see nil.
Please help to fix this issue.
Updated.
Code below - is a simple function to creating user document in firestore in my app
func signup(username: String, email: String, password: String, imageData: Data, completed: #escaping(_ user: User) -> Void, onError: #escaping(_ errorMessage: String) -> Void) {
if !username.isEmpty && !email.isEmpty && !password.isEmpty && !imageData.isEmpty {
AuthService.signupUser(username: username, email: email, password: password, imageData: imageData, onSuccess: completed, onError: onError)
} else {
if username == "" {
errorString = "Please enter your name"
onError(errorString)
// showAlert = true
}
if email == "" {
errorString = "Please enter yor valid email"
onError(errorString)
// showAlert = true
}
if password == "" {
errorString = "Please create password"
onError(errorString)
// showAlert = true
}
if image == Image(IMAGE_USER_PLACEHOLDER) {
errorString = "Please upload your avatar"
onError(errorString)
// showAlert = true
}
}
}
and here is AuthService.signupUser method
static func signupUser(username: String, email: String, password: String, imageData: Data, onSuccess: #escaping(_ user: User) -> Void, onError: #escaping(_ errorMessage: String) -> Void) {
//Firebase.createAccount(username: username, email: email, password: password, imageData: imageData)
Auth.auth().createUser(withEmail: email, password: password) { (authData, error) in
if error != nil {
print(error!.localizedDescription)
onError(error!.localizedDescription)
return
}
guard let userId = authData?.user.uid else { return }
let storageAvatarUserId = Ref.STORAGE_AVATAR_USERID(userId: userId)
let metadata = StorageMetadata()
metadata.contentType = "image/jpg"
StorageService.saveAvatar(userId: userId, username: username, email: email, imageData: imageData, metadata: metadata, storageAvatarRef: storageAvatarUserId, onSuccess: onSuccess, onError: onError)
}
}
I know that apple sign in cannot take users image & it's ok - my cloud trigger fill this field in document users uid & in my app if user avatar = nil - it's takes universal clipart, It's ok, newer mind.
And this is User file
import Foundation
struct User: Encodable, Decodable {
var uid: String
var email: String
var profileImageUrl: String
var username: String
var bio: String
var keywords: [String]
var status: String?
}
my security rules in firebase are simple, here they are
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
This issue is - that Apple don’t get username.
Issue is solved.
I am trying to update the isEmailVerified property without logging out of the application. So far I've been able to only update it when I log out of the application.
Here is what I have so far:
struct ContentView: View {
#EnvironmentObject var session: SessionStore
var body: some View {
Group {
if (self.session.session != nil) {
let user = Auth.auth().currentUser
VStack{
Text("Logged In As \(user?.email ?? "")")
Text("\(String(self.session.session!.isEmailVerified!))")
Button(action: {user?.sendEmailVerification { (error) in
}}){
Text("Verify email")
}
Button(action: {session.signOut()}){
Text("Sign Out")
}
}
} else {
OnBoardingView()
}
}.onAppear(perform: {
session.listen()
})
}
}
and
struct User {
var uid: String
var email: String?
var isEmailVerified: Bool?
init(uid: String, displayName: String?, email: String?, isEmailVerified: Bool?) {
self.uid = uid
self.email = email
self.isEmailVerified = isEmailVerified
}
}
class SessionStore : ObservableObject {
var didChange = PassthroughSubject<SessionStore, Never>()
var isLoggedIn = false { didSet { self.didChange.send(self) }}
#Published var session: User? { didSet { self.didChange.send(self) }}
var handle: AuthStateDidChangeListenerHandle?
init(session: User? = nil) {
self.session = session
}
func listen () {
// monitor authentication changes using firebase
handle = Auth.auth().addStateDidChangeListener { (auth, user) in
if let user = user {
// if we have a user, create a new user model
print("Got user: \(user)")
self.isLoggedIn = true
DispatchQueue.main.async {
self.session = User(
uid: user.uid,
displayName: user.displayName,
email: user.email,
isEmailVerified: user.isEmailVerified
)
}
} else {
// if we don't have a user, set our session to nil
self.isLoggedIn = false
self.session = nil
}
}
}
#discardableResult func signOut () -> Bool {
do {
try Auth.auth().signOut()
// self.isLoggedIn = false
// self.session = nil
return true
} catch {
return false
}
}
func signUp (
email: String,
password: String,
handler: #escaping AuthDataResultCallback
)
{Auth.auth().createUser(withEmail: email, password: password, completion: handler)}
func signIn (
email: String,
password: String,
handler: #escaping AuthDataResultCallback
)
{Auth.auth().signIn(withEmail: email, password: password, completion: handler)}
}
So far the two solutions I've gotten are that I need to refresh the user object using the following:
Auth.auth()?.currentUser.reload()
Or I can force it to get a new ID token using:
func getIDTokenResult(forcingRefresh forceRefresh: Bool, completion: ((FIRAuthTokenResult?, Error?) -> Void)? = nil)
However, since I'm new to coding I'm not sure I would implement this in my code. Any help would be appreciated. Thanks
I know this is quite an old discussion thread, but it seems no one has answered your question. If I am understanding your issue correctly, you can refresh the .isEmailVerified value (in Swift 4) like so:
Auth.auth().currentUser?.reload(completion: { (error) in
if let error = error {
// There was an error reloading the currentUser object
// Do something with the error here
} else {
// Success!
// Do anything you wanted to do with the refreshed .isEmailVerified in here!
print("The refreshed .isEmailVerified = \(Auth.auth().currentUser?.isEmailVerified)")
}
})
I want to gather information about user with firebase firestore, I have in a Class a function which do that, but I want share this information with my struct ContentView.
class UserData : ObservableObject {
// Use Firebase library to configure APIs
#Published var db = Firestore.firestore()
#Published var WholeDocument : Array<String> = []
func GetAllData(completion: #escaping (Array<String>) -> ()) {
db.collection("Users").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
completion([""])
} else {
for document in querySnapshot!.documents {
//print("\(document.documentID) => \(document.data())")
self.WholeDocument.append(document.documentID)
}
completion(self.WholeDocument)
}
}
I try to gather this array in my contentView
func Connection()
{
self.Database.GetAllData{(tab) in
if tab.count > 0 {
self.WholeDocumentContentView = tab
}
else {
print("Not found")
}
}
}
How to retrieve WholeDocument array with closure ?
Thank you for the help.
If I'm understanding the structure/question correctly, your goal is to access the WholeDocument array in another SwiftUI view. The way to do that could be:
class UserData : ObservableObject {
// Use Firebase library to configure APIs
private var db = Firestore.firestore()
#Published var WholeDocument : Array<String> = []
init() {
db.collection("Users").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
completion([""])
} else {
for document in querySnapshot!.documents {
// print("\(document.documentID) => \(document.data())")
self.WholeDocument.append(document.documentID)
}
}
}
}
}
struct MyContentView : View {
#ObservedObject var database: UserData
var body: some View {
// access database.WholeDocument
List(database.WholeDocument, id: \.self) {
// do something
}
}
That way, when you initialize MyContentView with an instance of UserData, you will be able to observe WholeDocument in that view.