0

我正在使用 CloudKit(默认的公共数据库)来存储有关两支球队之间比赛的信息。我正在尝试使用带有 async/await 的 Swift Concurrent 方法来检索比赛和团队数据。

比赛被存储为带有一些元数据的“Match” RecordType,比如比赛的年份(“matchYear”)。“比赛”记录类型还包含一个名为“团队”的记录类型的参考列表(每场比赛两个团队)。团队名称存储在“团队”记录类型中(字段“团队名称”)

我的 CloudKit 架构的粗略描述

“Match” Record Type
Fields:
   matchYear: Int64
   teams: Reference (List)
   …

“Team” Record Type
Fields:
   teamName: String
   …

使用以下代码,我可以从 CloudKit 读取所有“匹配”记录,并将该列表存储到 MatchRecords 数组中,然后我可以使用它来显示匹配列表(使用 SwiftUI,如果这有什么不同的话)。结果数组还包含对每场比赛的球队的引用。

struct MatchRecord {
    var recordID: CKRecord.ID
    var matchYear: Int
    var teams: [CKRecord.Reference]
    …
}

…

    private lazy var container = CKContainer.default()
    private lazy var database = container.publicCloudDatabase
…

    @MainActor func loadMatchList() async throws -> [MatchRecord] {
        let predicate = NSPredicate(value: true)
        let sortDescriptors = [NSSortDescriptor(key: “matchYear", ascending: false),NSSortDescriptor(key: "___createTime", ascending: false)]
        let query = CKQuery(recordType: “Match”, predicate: predicate)
        query.sortDescriptors = sortDescriptors
        let (matchResults, _) = try await database.records(matching: query)

        let allMatches: [MatchRecord] = matchResults
            .compactMap { _, result in try? result.get() }
            .compactMap {
                let match = MatchRecord(
                    recordID: $0.recordID,
                    matchYear: $0[“matchYear"] as! Int,
                    teams: $0["teams"] as! [CKRecord.Reference])
                // Potentially team record retrieval goes here…
                return match
            }
        return allMatches
    }

作为此异步功能的一部分,我如何检索团队记录,以便我也将获得可用于列表视图的团队名称?

(我可能首先获取匹配列表,然后遍历该数组并检索每个匹配的详细数据,但这似乎很浪费。我猜应该有一种方法可以将它插入到代码示例中标记的 compactMap 闭包中上面,但是我的 map/reduce 和 async/await 技能让我失望了……)

数据结构可能如下所述。

struct MatchRecord {
    var recordID: CKRecord.ID
    var matchYear: Int
    var teams: [TeamRecord]
    …
}

struct TeamRecord {
    var recordID: CKRecord.ID
    var teamName: String
    …
}

FWIW我知道因为每场比赛只有两支球队,我也可以将球队名称存储为比赛记录的一部分,但将来我计划还包括每支球队的名单信息,所以我需要来用一种干净且可扩展的方法从 CloudKit 中检索这种类型的分层数据……</p>

在这里,将 CoreData 与 CloudKit 一起使用不是一个选项。

4

1 回答 1

0

经过一番思考,我想出了一个使用异步属性的有点丑陋的解决方案。Match 和 Team 结构定义大致是这些

struct MatchRecord {
    var recordID: CKRecord.ID
    var matchYear: Int
    var teams: [TeamRecord]
    …
}
struct TeamRecord {
    var referenceID: CKRecord.Reference
    var name: String {
        get async {
            try! await CKContainer.default().publicCloudDatabase.record(for: referenceID.recordID)["teamName"] as! String
        }
    }
}

compactMap 在其中变得很小的 for 循环以填充团队结构(仅存储 ID,因为名称仅在以后检索)

            .compactMap {
                let match = MatchRecord(
                    recordID: $0.recordID,
                    matchYear: $0[“matchYear"] as! Int,
                    teams: [])
                for item in ($0["teams"] as! [CKRecord.Reference]) {
                    match.teams.append(TeamRecord(referenceID: item))
                }
                return match
            }

并且在显示列表时,列表行在单独的视图中定义,其中异步属性是使用任务拉入的。沿着这些方向

struct MatchRow: View {
...
    @State var team0: String = ""
    @State var team1: String = ""

    var body: some View {
...
        Text(verbatim: "\(match.matchYear): \(team0) - \(team2)")
            .task{
                guard team0.isEmpty else { return }
            
                let (team0Result, team1Result) = await (match.teams[0].name, match.teams[1].name)
                team0 = team0Result
                team1 = team1Result
            }
        }
    }
}

我相信有一个更优雅的解决方案。我特别不喜欢在列表行视图中添加任务的想法,因为这会在很多地方分割逻辑......

于 2021-12-28T23:40:19.453 回答