onAppear 中的 SwiftUI 异步数据获取

Posted

技术标签:

【中文标题】onAppear 中的 SwiftUI 异步数据获取【英文标题】:SwiftUI Async data fetch in onAppear 【发布时间】:2020-08-09 19:24:58 【问题描述】:

我有 getDataFromDatabase 类,它有 func readData() 那是从 Firebase 读取数据。

class getDataFromDatabase : ObservableObject 

    var arrayWithQuantity = [Int]()
    var arrayWithTime = [Double]()
    
    func readData(completion: @escaping(_ getArray: Array<Int>?,_ getArray: Array<Double>?) -> Void) 
       
        let db = Firestore.firestore()
        
        db.collection("amounts").getDocuments  (querySnapshot, err) in
            
            if let e = err
                print("There's any errors: \(e)")
            
            
            if err != nil
                print((err?.localizedDescription)!)
                return
            
            
            for i in querySnapshot!.documents
                
                let quantityFromDb = i.get("amount") as! Int
                let timeFromDb = i.get("averageTimeRecognition") as! Double
                
                
                self.arrayWithQuantity.append(quantityFromDb)
                self.arrayWithTime.append(timeFromDb)
            
            completion(self.arrayWithQuantity, self.arrayWithTime)
        
    

我在onAppear中使用func readData()

struct CheckDatabaseView: View 
    
    @State private var quantityFromDatabase: Array<Int> = []
    @State private var timeFromDatabase: Array<Double> = []
    @State private var flowersName: Array<String> = ["Carnation", "Daisy", "Hyacinth", "Iris", "Magnolia", "Orchid", "Poppy", "Rose", "Sunflower", "Tulip"]
    @State private var isReady: Bool = false
    
    
    var body: some View 
        
        ScrollView(.vertical, showsIndicators: false)
            ZStack(alignment: .top)
                VStack(spacing: 40)
                    Text("Hello, world!")
//                    BarView(value: CGFloat(timeFromDatabase[0]), name: flowersName[0])
                    
                
            
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
            
        
        .navigationBarTitle(Text("Your datas in database").foregroundColor(.blue), displayMode: .inline)
        .onAppear
            
            let gd = getDataFromDatabase()
            gd.readData  (quantity, time) in
                self.quantityFromDatabase = quantity!
                self.timeFromDatabase = time!       
            
        
    

我不能使用值 self.quantityFromDatabaseself.timeFromDatabase 因为它们是空的。我知道问题在于数据的异步检索。我试过DispatchQueue.main.async,但我仍然没有得到这些值。另一种获取方法是什么?我需要这个值,因为我想在VStack(那里的注释行)中绘制图表。

编辑

正如@Rexhin Hoxha 在下面写的,我修改了代码,但我不确定方法是否正确。我通过在类 getDataFromDatabase 中添加 @Published 更改了 var arrayWithQuantity = [Int]()var arrayWithTime = [Double]()(现在是 GetDataFromDatabaseViewModel):

class GetDataFromDatabaseViewModel : ObservableObject 

    @Published var arrayWithQuantity = [Int]()
    @Published var arrayWithTime = [Double]()
    
    func readData() 
       
        let db = Firestore.firestore()
        
        db.collection("amounts").getDocuments  (querySnapshot, err) in
            
            if let e = err
                print("There's any errors: \(e)")
            
            
            if err != nil
                print((err?.localizedDescription)!)
                return
            
            
            for i in querySnapshot!.documents
                
                let quantityFromDb = i.get("amount") as! Int
                let timeFromDb = i.get("averageTimeRecognition") as! Double
                
                
                self.arrayWithQuantity.append(quantityFromDb)
                self.arrayWithTime.append(timeFromDb)
            
            print("Array with quantity: \(self.arrayWithQuantity.count)")
        
    

同样在我初始化的结构中 @ObservedObject var gd = GetDataFromDatabaseViewModel()onAppear 现在看起来像这样:

.onAppear
            
            self.gd.readData()
            print("Quantity after reading: \(self.gd.arrayWithQuantity.count)")
        

但在 onAppear 中打印仍然会打印一个空数组。我哪里做错了?

【问题讨论】:

我现在无法测试您的代码,但我肯定知道一件事。您不需要使用 DispatchQueue.main.async,因为 Firebase 调用在主线程中运行。 【参考方案1】:

所以问题出在您的完成处理程序中。它会在您检索数据之前返回。

解决方案是让您的数组@Published 并从视图中实时读取数据。您必须删除完成处理程序。

调用“onAppear()”上的函数并使用@ObservedObject 绑定到您的ViewModel (getDataFromDatabase)。这就是它在 SwiftUI 中的完成方式。

请将第一个字母大写并使用更通用的名称,例如“YouViewName”ViewModel。

你的名字适用于方法/函数,但不适用于类

【讨论】:

您好,谢谢您的回复!我编辑了帖子,你能看一下吗?我确定我做错了,但我不知道在哪里 哦,好的,我现在明白了...谢谢您的帮助,我将尝试在互联网上找到解决此问题的方法。希望有什么办法可以解决这个问题 我会阅读有关异步调用的信息,也许会找到任何解决方案。谢谢! 添加数组而不是范围。每当在那里添加新元素时,foreach 都会更新。再次 1...5 将尝试打印元素,但在异步调用中,元素稍后出现。除非您非常确定该元素已经存在,否则您可以使用机器人来执行此操作。 感谢您的帮助。我做了其他事情 - 我刚刚创建了 if(array.count != 0) 然后在 Text() 中设置值。它现在工作正常,感谢您的时间和有用的提示!

以上是关于onAppear 中的 SwiftUI 异步数据获取的主要内容,如果未能解决你的问题,请参考以下文章

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

SwiftUI 异步数据获取

SwiftUI onAppear 被调用两次

如何正确处理更新视图中的选取器(SwiftUI)

SwiftUI:onAppear 的奇怪行为

SwiftUI 视图 onAppear 事件调用回扫手势动作