Ad

Class Or View Isn't Updating

Basically, I have a class which fetches profile info from Firestore, but when I edit the profile and submit the changes and return to the profile view, the information isn't updated, but it is updated within Firestone, so I have to close the app and relaunch it for it to update within the app. I'm assuming my issue is because I am not recalling the class or the view isn't being updated? Here is my code for the profile view, and the class I call to fetch the data from Firestore.

ProfileView.swift

import SwiftUI
import FirebaseAuth
import FirebaseFirestore


struct ProfileView: View {
    let auth = Auth.auth()
    @ObservedObject private var user = MainUserDataModel() //pulls user data from firebase (located in fetchUserData.swift) works like: user.currentUser?.uid ?? ""
    @State private var firstName = ""
    @State private var lastName = ""
    @State private var email = ""
    
    var body: some View {
        VStack {
            Image("img")
                .resizable()
                .frame(width: 100, height: 100)
                .clipShape(Circle())
                .padding()
            Text("\(firstName) \(lastName)")
                .fontWeight(.bold)
        
            Text("\(email)")
                .font(.caption)
                .padding(5)
            NavigationLink("Edit Profile", destination: EditProfile())
                .foregroundColor(Color.pink)
            Form {
            
            }
            
        }
        .task {
            firstName = user.currentUser?.firstName ?? ""
            lastName = user.currentUser?.lastName ?? ""
            email = user.currentUser?.email ?? ""
            
        }
        //.navigationTitle("Profile")
    }
}


struct ProfileView_Previews: PreviewProvider {
    static var previews: some View {
        ProfileView()
    }
}

fetchUserData.swift

import Foundation
import FirebaseAuth
import Firebase
import FirebaseFirestore

struct User {
    let firstName, lastName, uid, email: String
    
}

class MainUserDataModel: ObservableObject {
    @Published var errorMessage = ""
    @Published var currentUser: User?

    init() {
        fetchUserData()
    }

    private func fetchUserData() {
        let auth = Auth.auth()
        guard let uid = auth.currentUser?.uid else { return }

        Firestore.firestore().collection("users").document(uid).getDocument { snapshot, error in
            if let error = error {
                self.errorMessage = "Failed to fetch current user: \(error)"
                print("Failed to fetch current user:", error)
                return
            }

            guard let data = snapshot?.data() else {
                self.errorMessage = "No data found"
                return

            }
            let firstName = data["firstName"] as? String ?? ""
            let lastName = data["lastName"] as? String ?? ""
            let uid = data["uid"] as? String ?? ""
            let email = data["email"] as? String ?? ""
            self.currentUser = User(firstName: firstName, lastName: lastName, uid: uid, email: email)
        }
    }

}
Ad

Answer

You're setting your @State variables before the fetchUserData has completed. It would be cleaner to remove the @State variables altogether and just rely on the @Published values from your MainUserDataModel:

struct ProfileView: View {
    let auth = Auth.auth()
    @ObservedObject private var user = MainUserDataModel()
    
    var body: some View {
        VStack {
            Image("mank")
                .resizable()
                .frame(width: 100, height: 100)
                .clipShape(Circle())
                .padding()
            if let currentUser = user.currentUser {
                Text("\(currentUser.firstName) \(currentUser.lastName)")
                  .fontWeight(.bold)
                Text("\(currentUser.email)")
                  .font(.caption)
                  .padding(5)
                NavigationLink("Edit Profile", destination: EditProfile())
                .foregroundColor(Color.pink)
            } else {
               //Display a loading indicator
            }
        }
    }
}

You could also use your previous strategy of providing default values:

struct ProfileView: View {
    let auth = Auth.auth()
    @ObservedObject private var user = MainUserDataModel()
    
    var body: some View {
        VStack {
            Image("mank")
                .resizable()
                .frame(width: 100, height: 100)
                .clipShape(Circle())
                .padding()
            Text("\(user.currentUser?.firstName ?? "") \(user.currentUser?.lastName ?? "")")
                  .fontWeight(.bold)
            Text("\(user.currentUser?.email ?? "")")
                  .font(.caption)
                  .padding(5)
            NavigationLink("Edit Profile", destination: EditProfile())
                .foregroundColor(Color.pink)
        }
    }
}

If instead, you really need the @State variables, you shouldn't set them until your MainUserDataModel loads the data -- you can use onReceive and watch for changes to the currentUser publisher.

Ad
source: stackoverflow.com
Ad