【问题标题】:Firestore method in view model not invoking in SwiftUI onAppear method视图模型中的 Firestore 方法未在 SwiftUI onAppear 方法中调用
【发布时间】:2021-04-23 21:11:15
【问题描述】:

我想实现一个文本字段来显示当前用户在数据库(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()
        }
    }
}
// View.swift

import SwiftUI
import CoreData
import Firebase

{
    @ObservedObject private var users = UserViewModel()
    var body: some View {
        VStack {
            HStack {
                // ...
                Text("xp: \(users.xp ?? 0)")
//                    Text("xp: 1500")
                    .fontWeight(.bold)
                    .padding(.horizontal)
                    .foregroundColor(Color.white)
                    .background(Color("Black"))
                    .clipShape(CustomCorner(corners: [.bottomLeft, .bottomRight, .topRight, .topLeft], size: 3))
                    .padding(.trailing)
            }
            .padding(.top)
            .onAppear() {
                self.users.fetchData()
            }
// ...
    }
}

我的结果在Text("xp: \(users.xp ?? 0)") 中一直显示为 0,这表示该步骤尚未异步。那我该怎么解决呢?

【问题讨论】:

  • 您没有显示fetchData 的调用位置。另外,您确定self.xp 被分配了吗?您是否设置了断点或打印语句以确保获得您期望的值?
  • 在这种情况下完成处理程序应该做什么也不是很清楚。您的视图将自动响应 @Published 属性,进一步强化了代码实际上可能没有成功到达或分配 self.xp 的想法
  • 好吧,我只是相信因为 Firebase 总是异步检索数据,completion() 处理程序有助于指示此步骤需要在任何其他步骤之前完成。你的意思是completion()在这里没用吗?
  • 您在编辑中添加的调用站点似乎甚至无效(它没有为完成处理程序提供参数。所以,是的,出于这个原因和我上面说了,这里好像没啥用。

标签: swift firebase asynchronous google-cloud-firestore swiftui


【解决方案1】:

在进一步调试之前,我会先检查以确保 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)")
            }
        }
    }
}

SwiftUI View - viewDidLoad()? 是你最终要解决的问题。

【讨论】:

  • 老实说,我看不出你的答案和我的有什么区别。更不用说您使用guard let 的声明是非法的。
  • 那你有数据问题、网络问题或授权问题,所以从列表中删除代码并继续调试。
  • 对谁不合法?
  • 确保currentUser? 是可选的链式,而不是像你拥有的那样强制解包:guard let uid = Auth.auth().currentUser?.uid
  • 阅读接受的答案中最受好评的评论,您将看到最终需要在哪里解决问题,找到正确的位置来调用获取数据的调用(如果初始化程序不在您的位置想做)。 stackoverflow.com/questions/56496359/swiftui-view-viewdidload
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-01
  • 2015-02-10
  • 1970-01-01
  • 2016-04-26
  • 1970-01-01
  • 2016-11-04
  • 2015-12-18
相关资源
最近更新 更多