嵌套 CloudKit 查询和 UITableView

Nested CloudKit queries and UITableView

我正在尝试使用 CloudKit 制作一个简单的 table-based 应用程序,它允许您向下钻取以查找有关特定项目的更多信息。假设我的应用程序显示了位于主题公园内的商店目录,按公园的部分划分。

在我最初的 table 视图中,我能够获得 ThemeParks 的列表。点击 ThemePark 会向下钻取到另一个 table 视图,该视图具有公园每个部分的部分 header 标题。我能够查询具有 CKReference 匹配所选 ThemePark 的部分。当我尝试获取与每个部分匹配的商店时,就会出现问题。我不知道如何等到商店全部下载后再重新加载 table 视图。这是我的视图控制器中的一些示例代码:

func getSectionsFromCloud() {
    let cloudContainer = CKContainer.defaultContainer()
    let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
    let recordToMatch = CKReference(recordID: themePark.recordID, action: CKReferenceAction.DeleteSelf)
    let predicate = NSPredicate(format: "themeParkOwner == %@", recordToMatch)
    let query = CKQuery(recordType: "ThemeParkSection", predicate: predicate)
    let sort = NSSortDescriptor(key: "name", ascending: true)
    query.sortDescriptors = [sort]
    self.publicDatabase.performQuery(query, inZoneWithID: nil, completionHandler: {
        results, error in

        if error == nil {
            println("Successfully retrieved sections")
            self.sections = results as [CKRecord]
            for section in self.sections {
                self.getShopsFromCloud(section)
            }

        } else {
            println(error)
        }
    })
}

这是我的 getShopsFromCloud 函数:

func getShopsFromCloud(record:CKRecord) {
    let cloudContainer = CKContainer.defaultContainer()
    let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
    let recordToMatch = CKReference(recordID: record.recordID, action:CKReferenceAction.DeleteSelf)
    let predicate = NSPredicate(format: "sectionOwner == %@", recordToMatch)
    let query = CKQuery(recordType: "Shop", predicate: predicate)
    self.publicDatabase.performQuery(query, inZoneWithID: nil, completionHandler: {
            results, error in

        if error == nil {
            println("Successfully retrieved shops.")
            self.shops.append(results as [CKRecord])

        } else {
            println(error)
        }
    })
}

问题是,getSectionsFromCloud() 在所有 getShopsFromCloud() 调用之前完成。因此,如果我在 getSectionsFromCloud 的 completionHandler 中重新加载 table 视图,它不会显示商店。但是,如果我在 completionHandler 中为 getShopsFromCloud 重新加载 table 视图,则每次执行查询时(每个部分执行一次),numberOfRowsInSection 也会被调用一次对于每个部分。所以如果我的 numberOfRowsInSection 是这样的:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return self.shops[section].count
}

...我收到 "fatal error: array index out of range" 错误,因为第一个商店数组已下载,但随后为第二个数组调用 numberOfRowsInSection,该数组还不存在。那么我怎么知道 getShopsFromCloud() 什么时候结束以及我应该把那个 tableView.reloadData() 调用放到什么地方呢?

我认为解决此问题的最简单方法是在创建 self.sections 数组的同时创建 self.shops 数组。只需确保每个 self.shops 项目还包含一个空数组。这样计数将 return 0 因为列表还没有被获取。在 getShopsFromCloud 中执行 reloaddata 是正确的方法。你必须知道你需要在主队列上执行 reloaddata。

尝试使用获取请求之间的依赖关系并将它们添加到数据库对象的队列 (CKContainer.defaultContainer().publicDatabase().addOperation) 而不是调用便捷方法