简体   繁体   English

SwiftUI onAppear 中的异步数据获取

[英]SwiftUI Async data fetch in onAppear

I have class getDataFromDatabase which has func readData() thats read data from Firebase.我有 class 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)
        }
    }
}

I use func readData() in onAppear :我在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!       
            }
        }
    }
}

I cannot use values self.quantityFromDatabase and self.timeFromDatabase because are empty.我不能使用值self.quantityFromDatabaseself.timeFromDatabase因为是空的。 I know the problem is with the asynchronous retrieval of data.我知道问题在于数据的异步检索。 I've tried with DispatchQueue.main.async , but I still not get these values.我已经尝试过DispatchQueue.main.async ,但我仍然没有得到这些值。 How is the other method to get it?另一种获取方法是什么? I need this values, because I want to draw charts in VStack (the comment line there).我需要这个值,因为我想在VStack (那里的注释行)中绘制图表。

EDIT编辑

As @Rexhin Hoxha wrote below, i modified the code but i am not sure if the way is correct.正如@Rexhin Hoxha 在下面写的那样,我修改了代码,但我不确定方法是否正确。 I changed var arrayWithQuantity = [Int]() and var arrayWithTime = [Double]() by adding @Published in class getDataFromDatabase (now it's GetDataFromDatabaseViewModel):我通过在 class getDataFromDatabase(现在是 GetDataFromDatabaseViewModel)中添加@Published更改了var arrayWithQuantity = [Int]()var arrayWithTime = [Double]() ):

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)")
        }
    }
}

also in struct I initialized @ObservedObject var gd = GetDataFromDatabaseViewModel() and onAppear now looks like this:同样在 struct 我初始化@ObservedObject var gd = GetDataFromDatabaseViewModel()onAppear现在看起来像这样:

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

but print in onAppear still print an empty Array.但在 onAppear 中打印仍然打印一个空数组。 Where did I do a mistake?我在哪里做错了?

So the problem is in your completion handler.所以问题出在你的完成处理程序中。 It returns before you retrieve the data.它在您检索数据之前返回。

Solution is to make your arrays @Published and read the data in real time from the view.解决方案是让您的 arrays @Published 并从视图中实时读取数据。 You have to remove the completion handler.您必须删除完成处理程序。

Call the function on ‚onAppear()' and use @ObservedObject to bind to your ViewModel (getDataFromDatabase).在“onAppear()”上调用 function 并使用 @ObservedObject 绑定到您的 ViewModel (getDataFromDatabase)。 This is how it's done in SwiftUI.这就是它在 SwiftUI 中的完成方式。

Please capitalize the first letter and use something more generic like „YouViewName“ViewModel.请大写第一个字母并使用更通用的名称,例如“YouViewName”ViewModel。

Your name is fine for a method/function but not for a Class您的名字适用于方法/功能,但不适用于 Class

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM