为什么我必须从我的单例中设置@ObservedObject 引用来更新数据?

Why do I have to set @ObservedObject reference from my singleton to update data?

这段代码是一个简单的视图。我有一个名为 UserProfile 的单例,它在应用程序生命周期的早期被调用。当用户注册、登录或登录时,将设置该用户配置文件的详细信息。然后这个视图最终呈现给用户。有问题的代码是 @ObservedObject var profile = UserProfile.sharedProfile。如果我删除那行代码,Text(...) 视图将不会显示任何内容。如果我有那行代码,它们就可以正常工作。单例在到达我的基本视图时在应用程序启动时被初始化。

如何以更好的方式做到这一点,以便我始终可以访问 UserProfile.sharedProfile

struct BaseHomeScreenView: View {
    @ObservedObject var baseHomeScreenViewModel = BaseHomeScreenViewModel()
    //This object is not being used anywhere but it does affect the view below.
    @ObservedObject var profile = UserProfile.sharedProfile
    
    var body: some View {
        ZStack {
            VStack {
                Text("Hello")
                Text(UserProfile.sharedProfile.firstName)
                Text(UserProfile.sharedProfile.lastName)
                Text(UserProfile.sharedProfile.email)
                Image(uiImage: UserProfile.sharedProfile.profileImage)
            }
        }
    }
}

第一次查看我的应用程序

class BaseViewModel: ObservableObject {
    @Published var userFlow: UserFlow = .login
    
    init(){
        if Auth.auth().currentUser != nil {
            userFlow = .home
            UserProfile.sharedProfile.fetchProfileInfo()
        } else {
            userFlow = .login
        }
    }
    
    enum UserFlow {
        case onboarding, login, home
    }
}

Singleton/UserProfile

class UserProfile: ObservableObject {
    static var sharedProfile = UserProfile()
    
    @Published var profileImage: UIImage
    @Published var firstName: String
    @Published var lastName: String
    @Published var email: String
    
    init() {
        self.profileImage = UIImage()
        self.firstName = ""
        self.lastName = ""
        self.email = ""
    }
        
    func setUserData(profileImageURL: String, firstName: String, lastName: String, email: String) {
        fetchImageFromURL(url: URL(string: profileImageURL)!)
        self.firstName = firstName
        self.lastName = lastName
        self.email = email
    }
    
    func fetchProfileInfo() {
        guard let uid = Auth.auth().currentUser?.uid else {
            print("Error: UserProfile.fetchProfileInfo() - Failed to fetch user id.")
            return
        }
        let db = Firestore.firestore()
        let fsUserProfile = db.collection("users").document(uid)
        
        fsUserProfile.getDocument { (snapshot, err) in
            if err != nil { return }
            self.fetchImageFromURL(url: URL(string: snapshot?.get("profile_image_url") as? String ?? "")!)
            self.firstName = snapshot?.get("first_name") as? String ?? ""
            self.lastName = snapshot?.get("last_name") as? String ?? ""
            self.email = snapshot?.get("email") as? String ?? ""
        }
    }
    
    func fetchImageFromURL(url: URL) {
        getData(from: url) { data, response, error in
                guard let data = data, error == nil else {
                    if let error = error {
                        print("Error: UserProfile.fetchImageFromURL() - Failed to fetch image from URL. | \(error.localizedDescription)")
                    } else {
                        print("Error: UserProfile.fetchImageFromURL() - Failed to get data while fetching image.")
                    }
                    return
                }
                DispatchQueue.main.async() {
                    self.profileImage =  UIImage(data: data)!
                }
            }
    }
    
    private func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
        URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
    }
}

SwiftUI 需要知道什么时候发生了变化,以便重新计算依赖于变化的视图的 body

对于对象,它使用 @ObservedObject 属性 包装器(或 @StateObject,或 @EnvironmentObject),并且对象必须符合 ObservableObject .当该对象发出更改信号时 - 通过更新其 @Published 属性 或直接调用 objectWillChange.send() - SwiftUI 知道更新视图。


因此,在您的示例中,当 @ObservedObject var profile 属性“更改”时,body 被重新计算,并通过重新计算读取 UserProfile.sharedProfile 及其属性。