swift - onAppear 中的 SwiftUI 异步数据获取
问题描述
我有一个类getDataFromDatabase
,它具有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)
}
}
}
我在 funcreadData()
中使用onAppear
:
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.quantityFromDatabase
,self.timeFromDatabase
因为是空的。我知道问题在于数据的异步检索。我试过了DispatchQueue.main.async
,但我仍然没有得到这些值。另一种获取方法是什么?我需要这个值,因为我想在VStack
(那里的注释行)中绘制图表。
编辑
正如@Rexhin Hoxha 在下面写的那样,我修改了代码,但我不确定方法是否正确。我更改var arrayWithQuantity = [Int]()
并var arrayWithTime = [Double]()
添加@Published
了类 getDataFromDatabase(现在是 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 中打印仍然打印一个空数组。我在哪里做错了?
解决方案
所以问题出在你的完成处理程序中。它在您检索数据之前返回。
解决方案是使您的数组@Published 并从视图中实时读取数据。您必须删除完成处理程序。
调用“onAppear()”上的函数并使用@ObservedObject 绑定到您的ViewModel (getDataFromDatabase)。这就是它在 SwiftUI 中的完成方式。
请大写第一个字母并使用更通用的名称,例如“YouViewName”ViewModel。
你的名字适用于方法/函数,但不适用于类
推荐阅读
- c# - 单元测试分组“项目、命名空间、类”被 TestCase.TestName 中的点拆除
- java - 如何在 Spring Boot 上检测互联网连接?
- html - 有没有办法将html并排放置
- rust - 避免锁定单线程异步
- php - 将 sql 转换为 dql symfony
- vba - 如何在 selenium 中使用 vba 选择我们在下拉列表中输入的文本?
- flutter - 为 DateTime 声明 obs 类型的正确方法是什么?
- python - 将 Pandas 类别分配给新号码
- hyperledger-fabric - 如何在没有织物样本的情况下启动超级账本织物?
- c# - 使用链接数据参数调用方法助手