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