为什么我必须从我的单例中设置@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
及其属性。
这段代码是一个简单的视图。我有一个名为 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
及其属性。