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("mank")
.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)
}
}
}
CodePudding user response:
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.