Async/Await 函数不加载数据
Async/Await function not not loading data
我在 Swift/SwiftUI 中有以下复制示例。我只是想从 firebase 中提取 2 个“评分”,然后在加载后显示它们。我有 2 种可能的加载评分的方法。
方法一:等待数据加载,加载完成后显示。但是,我的问题是这段代码没有加载数据,我的评级列表仍然是空的。不过,数据库没有抛出任何错误。
方法二:不用等待数据加载,而是用定时器简单地等待2秒,希望数据在显示之前加载完毕。
方法 1 是我正在尝试的方法。然而,方法 2 有效,所以我知道这不是数据库或我的数据的问题,但很可能是我的 async/await 使用问题。
下面是我复制的代码。看起来很多,但不是很疯狂,抱歉!
struct test: View {
@State var ratings: [Double] = []
@State var ratingsAreLoading = true
func getRatings() async {
let db = Firestore.firestore() //database object
await db.collection("users").document("user 1").getDocument { (doc, error) in
if let doc = doc { //just gets some data
ratings.append(doc["rating1"] as? Double ?? 1.0)
ratings.append(doc["rating2"] as? Double ?? 1.0)
}
}
}
var body: some View {
VStack {
if ratingsAreLoading {
Text("LOADING...")
} else {
Text(String(ratings[0])) //displays a rating
}
}
//IMPLEMENTATION HERE
}
}
方法 1:似乎没有填充“评分”列表
.task {
await getRatings() //gets ratings
await MainActor.run {
ratingsAreLoading = false //displays the ratings
}
}
方法 2:有效
.onAppear {
getRatings() //gets ratings
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
ratingsAreLoading = false //displays ratings after 2 seconds
}
}
async/await 方法我做错了什么?我怎样才能让它及时加载我的数据?
您必须将完成处理程序转换为与 async await
兼容的 Continuation
看起来像这样
///Retrieves a single document with the provided id at the collection's path
public func retrieve<FC : FirestoreCodable>(path: String, id: String) async throws -> FC{
typealias MyContinuation = CheckedContinuation<FC, Error>
return try await withCheckedThrowingContinuation { (continuation: MyContinuation) in
let docRef = store.collection(path).document(id)
docRef.getDocument(as: FC.self){
result in
switch result{
case .success(let object):
continuation.resume(returning: object)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
为了补充上面的代码,我在 class
/struct
级别放置了一个 属性
private let store : Firestore
这样您就可以在 init
中添加任何设置代码,例如 isPersistenceEnabled
FirestoreCodable
只是一个 protocol
来帮助泛型
public protocol FirestoreCodable: Decodable, Encodable{
/// Wrap with `@DocumentID` from `import FirebaseFirestoreSwift`
var id: String? { get set }
}
你的struct
看起来像
struct User: FirestoreCodable{
@DocumentID var id: String?
var rating1: Double
var rating2: Double
}
你会像这样使用它
let user: User = try await retrieve(path: "users", id: "user 1")
您可以找到更多关于 async
/await
的 WWDC21 视频
https://developer.apple.com/wwdc21/10132
我在 Swift/SwiftUI 中有以下复制示例。我只是想从 firebase 中提取 2 个“评分”,然后在加载后显示它们。我有 2 种可能的加载评分的方法。
方法一:等待数据加载,加载完成后显示。但是,我的问题是这段代码没有加载数据,我的评级列表仍然是空的。不过,数据库没有抛出任何错误。
方法二:不用等待数据加载,而是用定时器简单地等待2秒,希望数据在显示之前加载完毕。
方法 1 是我正在尝试的方法。然而,方法 2 有效,所以我知道这不是数据库或我的数据的问题,但很可能是我的 async/await 使用问题。
下面是我复制的代码。看起来很多,但不是很疯狂,抱歉!
struct test: View {
@State var ratings: [Double] = []
@State var ratingsAreLoading = true
func getRatings() async {
let db = Firestore.firestore() //database object
await db.collection("users").document("user 1").getDocument { (doc, error) in
if let doc = doc { //just gets some data
ratings.append(doc["rating1"] as? Double ?? 1.0)
ratings.append(doc["rating2"] as? Double ?? 1.0)
}
}
}
var body: some View {
VStack {
if ratingsAreLoading {
Text("LOADING...")
} else {
Text(String(ratings[0])) //displays a rating
}
}
//IMPLEMENTATION HERE
}
}
方法 1:似乎没有填充“评分”列表
.task {
await getRatings() //gets ratings
await MainActor.run {
ratingsAreLoading = false //displays the ratings
}
}
方法 2:有效
.onAppear {
getRatings() //gets ratings
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
ratingsAreLoading = false //displays ratings after 2 seconds
}
}
async/await 方法我做错了什么?我怎样才能让它及时加载我的数据?
您必须将完成处理程序转换为与 async await
看起来像这样
///Retrieves a single document with the provided id at the collection's path
public func retrieve<FC : FirestoreCodable>(path: String, id: String) async throws -> FC{
typealias MyContinuation = CheckedContinuation<FC, Error>
return try await withCheckedThrowingContinuation { (continuation: MyContinuation) in
let docRef = store.collection(path).document(id)
docRef.getDocument(as: FC.self){
result in
switch result{
case .success(let object):
continuation.resume(returning: object)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
为了补充上面的代码,我在 class
/struct
级别放置了一个 属性
private let store : Firestore
这样您就可以在 init
中添加任何设置代码,例如 isPersistenceEnabled
FirestoreCodable
只是一个 protocol
来帮助泛型
public protocol FirestoreCodable: Decodable, Encodable{
/// Wrap with `@DocumentID` from `import FirebaseFirestoreSwift`
var id: String? { get set }
}
你的struct
看起来像
struct User: FirestoreCodable{
@DocumentID var id: String?
var rating1: Double
var rating2: Double
}
你会像这样使用它
let user: User = try await retrieve(path: "users", id: "user 1")
您可以找到更多关于 async
/await
的 WWDC21 视频
https://developer.apple.com/wwdc21/10132