视图模型中的Firestore方法未在SwiftUI onAppear方法中调用
我想实现一个文本字段,在DB(Firestore)中显示当前用户的现有分数。由于Firebase查询中异步的性质,我还需要对代码进行一些调整。但是,视图模型中的Firestore方法未在SwiftUI onAppear方法中调用,swift,firebase,asynchronous,google-cloud-firestore,swiftui,Swift,Firebase,Asynchronous,Google Cloud Firestore,Swiftui,我想实现一个文本字段,在DB(Firestore)中显示当前用户的现有分数。由于Firebase查询中异步的性质,我还需要对代码进行一些调整。但是,completion()处理程序似乎无法正常工作: // ViewModel.swift import Foundation import Firebase import FirebaseFirestore class UserViewModel: ObservableObject { let current_user_id = Auth
completion()
处理程序似乎无法正常工作:
// ViewModel.swift
import Foundation
import Firebase
import FirebaseFirestore
class UserViewModel: ObservableObject {
let current_user_id = Auth.auth().currentUser!.uid
private var db = Firestore.firestore()
@Published var xp:Int?
func fetchData(completion: @escaping () -> Void) {
let docRef = db.collection("users").document(current_user_id)
docRef.getDocument { snapshot, error in
print(error ?? "No error.")
self.xp = 0
guard let snapshot = snapshot else {
completion()
return
}
self.xp = (snapshot.data()!["xp"] as! Int)
completion()
}
}
}
我的结果一直在
文本(“xp:\(users.xp??0)”
中显示0,这表示该步骤尚未异步。那么我可以做些什么来解决它呢?在进一步调试之前,我会先检查Firestore控制台中的数据是否有效。也就是说,如果您使用的是可观察对象,并且应该安全地展开数据,那么就可以取消完成处理程序。错误总是会在网络呼叫中发生,因此总是安全地打开遇到的任何内容。另外,利用Firestore API中惯用的get()
方法,它使代码更易于阅读
也就是说,问题在于调用水平堆栈的onAppear
方法手动获取数据。这种模式可能会在SwiftUI中产生令人讨厌的结果,因此只需删除在视图中手动获取数据的调用,并在视图模型的初始值设定项中自动执行
class UserViewModel: ObservableObject {
@Published var xp: Int?
init() {
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let docRef = Firestore.firestore().collection("users").document(uid)
docRef.getDocument { (snapshot, error) in
if let doc = snapshot,
let xp = doc.get("xp") as? Int {
self.xp = xp
} else if let error = error {
print(error)
}
}
}
}
struct ContentView: View {
@ObservedObject var users = UserViewModel()
var body: some View {
VStack {
HStack {
Text("xp: \(users.xp ?? 0)")
}
}
}
}
是您最终想要解决的问题。您没有显示调用
fetchData
的位置。另外,您确定已分配self.xp
?您是否设置了断点或打印语句以确保获得预期的值?在这种情况下,还不清楚完成处理程序应该做什么。您的视图将自动响应@Published属性,这进一步强化了这样一种观点,即代码可能实际上并没有成功地到达或分配self.xp
,我只是相信,因为Firebase总是异步检索数据,completion()处理程序有助于指示此步骤需要在任何其他步骤之前完成。你的意思是说completion()在这里是无用的吗?你调用了你在编辑中添加的站点,但它似乎并不有效(它没有为完成处理程序提供参数。因此,是的,出于这个原因以及我上面提到的原因,它在这里似乎没有任何用处。老实说,我看不出你的答案和我的答案之间有任何区别。更不用说你使用guard let
的声明是非法的。那么你就有数据问题、网络问题或身份验证问题了。)请从列表中删除代码并继续调试。对谁非法?请确保currentUser?
是可选的链接,而不是像您那样强制展开:guard let uid=Auth.Auth().currentUser?.uid
阅读接受答案中投票最多的注释,您将看到最终需要解决问题的地方,找到调用以获取数据的正确位置(如果初始值设定项不在您希望的位置)。
class UserViewModel: ObservableObject {
@Published var xp: Int?
init() {
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let docRef = Firestore.firestore().collection("users").document(uid)
docRef.getDocument { (snapshot, error) in
if let doc = snapshot,
let xp = doc.get("xp") as? Int {
self.xp = xp
} else if let error = error {
print(error)
}
}
}
}
struct ContentView: View {
@ObservedObject var users = UserViewModel()
var body: some View {
VStack {
HStack {
Text("xp: \(users.xp ?? 0)")
}
}
}
}