视图模型中的Firestore方法未在SwiftUI onAppear方法中调用

视图模型中的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

我想实现一个文本字段,在DB(Firestore)中显示当前用户的现有分数。由于Firebase查询中异步的性质,我还需要对代码进行一些调整。但是,
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)")
            }
        }
    }
}