I am trying to make a simple table-based app using CloudKit
that allows you to drill down to find more information about a particular item. Let's say my app shows a directory of shops located inside theme parks, divided by sections of the park.
In my initial table view, I was able to get the list of ThemeParks. Tapping on a ThemePark drills down to another table view that has section header titles for each section of the park. I was able to query for the Sections that had a CKReference
matching the chosen ThemePark. The problem occurs when I try to get the Shops that match each Section. I have no clue how to wait until the shops are all downloaded before reloading the table view. Here is some sample code from my view controller:
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)
}
})
}
And here is my getShopsFromCloud
function:
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)
}
})
}
The problem is, getSectionsFromCloud()
finishes before all of the getShopsFromCloud()
calls. So if I reload the table view in the completionHandler for getSectionsFromCloud, it won't show the shops. However, if I reload the table view in the completionHandler
for getShopsFromCloud
, every time the query is executed (which is once for each section), numberOfRowsInSection
also gets called once for each section. So if my numberOfRowsInSection
is this:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.shops[section].count
}
...I get a "fatal error: array index out of range" error, because the first array of shops are downloaded, but numberOfRowsInSection
is then called for the second array, which isn't there yet. So how can I know when getShopsFromCloud()
finishes and where in the world do I put that tableView.reloadData()
call?
Try to use dependencies between fetch requests and add them to the Queue of the Database object (CKContainer.defaultContainer().publicDatabase().addOperation
) instead of calling the convenience method