Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialSimen Anthonsen
1,097 PointsCloudKit query
I have got a record type named Movies, and in my Public Data Default Zone I have created 10 movie objects. My objective is to query all 10 objects and append them to an array.
How do I do this?
3 Answers
ianhan3
4,263 Pointsvar moviesArray: [CKRecord] = []
func getCloudKitMoviesData() {
let cloudContainer = CKContainer.defaultContainer()
let publicDatabase = cloudContainer.publicCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Movies", predicate: predicate)
//operational API, you can use convenience API but I've never used it in a project since you're generally just pulling certain things.
let queryOperation = CKQueryOperation(query: query)
//ADD DESIRED KEYS IN ARRAY BELOW
queryOperation.desiredKeys = ["", "", ""]
queryOperation.queuePriority = .VeryHigh
queryOperation.recordFetchedBlock = {
(record: CKRecord!) -> Void in
if let moviesRecord = record {
//this is where you are appending to your array
self.moviesArray.append(moviesRecord)
}
}
queryOperation.queryCompletionBlock = {
(cursor:CKQueryCursor?, error: NSError?) -> Void in
//if there is an error, throw an error controller
if (error != nil) {
let controller = UIAlertController(title: "Error", message: "Error: \(error)", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default, handler: nil)
controller.addAction(action)
self.presentViewController(controller, animated: true, completion: nil)
}
NSOperationQueue.mainQueue().addOperationWithBlock() {
//Do anything else with the record after downloaded like assigning to properties or variables
}
}
}
publicDatabase.addOperation(queryOperation)
}
Simen Anthonsen
1,097 PointsThank you so much!!
One extra question: How do I access the properties/keys to the movie objects after they have been retrieved? My keys/field names include Title, Genre and rating. Like this.
myLabel.text = moviesArray[0].title
how do I manage this?
ianhan3
4,263 PointsUsing the moviesArray from before:
moviesArray[index].objectForKey("title") as? String
where the index is whatever index value you want to access, the "title" is the key name (could be genre, rating, etc) which you then cast as whatever you created in the CK Dashboard. Let's say you were building a tableView list of each movie with their properties and you set up labels like so:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MoviesTableViewCell
let movieReuseIndex = moviesArray[indexPath.row]
cell.genreLabel.text = movieReuseIndex.objectForKey("genre") as? String
// or use it with string interpolation
let rating = movieReuseIndex.objectForKey("rating") as? Int
cell.ratingLabel.text = "\(rating) stars"
return cell
}
Simen Anthonsen
1,097 PointsThanks so much man!
ianhan3
4,263 PointsFrom the documentation
"If the search yields many records, the operation object may deliver a portion of the total results to your blocks immediately, along with a cursor for obtaining the remaining records. If a cursor is provided, use it to initialize and execute a separate CKQueryOperation object when you are ready to process the next batch of results."
This is a good code example of it.
Simen Anthonsen
1,097 PointsThanks man, I have looked at it. I just have a hard time understanding it :S
ianhan3
4,263 PointsCloudKit's documentation is awful but can provide at least some clues generally. CK is just not old enough for every question to be answered. I actually got an app rejected 4 times because CK didn't work in the reviewers environment but worked in every other production capacity. I even got DTS involved and they couldn't replicate the problem. I would love to use it more but I definitely understand Apple doesn't make it simple.
Simen Anthonsen
1,097 PointsYeah I know. Look, I don´t want to be rude or bugging you, but if you know how to modify the code you gave me, the one listed as the answer of this thread, to make sure it retrieves all of my records I would greatly appreciate it. This is literally the only thing I have got left to do before submitting my first ever app.
ianhan3
4,263 Pointsianhan3
4,263 PointsSo let me walk you through this.
Don't forget to import CloudKit on top.
let cloudContainer = CKContainer(identifier: "iCloud.com.yourIdentifierName.yourAppName")
Public/ Private Database: Assign either the public or private database to a constant to use later. Access the private container the same way using dot syntax. Just use .privateCloudDatabase instead.
Predicate: Allows you to grab only certain record that meet certain criteria. I've used it in the simplest sense in a stock exchange app to grab only a certain record that equals a stock name. For example:
let predicate = NSPredicate(format: "stockSymbol = %@", stockSymbol)
where stockSymbol is a predefined variable. If you're just looking for them all, you can just define it like above with it's value set to true.
query: Create a CKQuery with your recordType name. In your case it's Movies. It's whatever the main titles of your record types are in the CK Dashboard.
query operation: Here's your actual operation. You wanted to append it to a new array so create an array outside the function (so you can use it later), set it empty, and append the results from the cloud in a fetchedBlock. Make sure to define your keys you want (the "field names" in your record types in you CK Dashboard). There is a convenience API as well where it just downloads everything from that recordType but rarely have I done this. IMO it's better to learn the operational as you'll have more use for it in your apps. In the completion, make sure to be able to handle errors and if no errors, do something (stop activity spinner, assign labels, etc.).
Don't forget to add the operation and run it to the publicDatabase contestant you created earlier. Don't forget to also run the function (generally in viewDidLoad). I've done that many times haha!
Simen Anthonsen
1,097 PointsSimen Anthonsen
1,097 PointsThank you so much!!
One extra question: How do I access the properties/keys to the movie objects after they have been retrieved? My keys/field names include Title, Genre and rating. Like this.
myLabel.text = moviesArray[0].title
how do I manage this?
Simen Anthonsen
1,097 PointsSimen Anthonsen
1,097 PointsHi again! You wouldn´t know how to skip past the standard 100 limit when fetching records?