When you use the CloudKit Dashboard or Swift code to add records in the CloudKit private or public database, CloudKit put them in the private database’s Default Zone or the public database’s Default Zone.
Private database’s Default Zone | Public database’s Default Zone |
Now, a zone is like a table in the CloudKit database and it act like a silo for records, letting you separate different types of data in the database. You use the CloudKit Dashboard Swift code to create additional zones only in the private database. If your data structure is simple, you can add records in the private and public database’s Default Zone.
In this tutorial you learn how to do the following tasks in the CloudKit’s private database:
- Create a custom zone
- Add records in the custom zone
- Display the custom zone records
- Edit a single record in the custom zone
- Delete a record from the custom zone
- Delete all records in the custom zone
Now, click the MainViewController.swift file to load it in the standard code editor, because you’ll be adding code in the file to implement above tasks.
Code presented on this page assume you are using Xcode 6.4 and Swift version 1.2. So if you are using a newer version of Swift, it may produce errors. Fix them by using Xcode’s Fix-it tool. I assume you are a registered member of Apple’s iOS Developer Program, and you have a real iPhone/iPad device to test the CloudKit code presented here. |
Create a custom zone
As you already know, you can only create additional zones only in the CloudKit’s private database. Let’s create one called “friendsZone” in the CloudKit’s private database. You do that by replacing the executeCodeButtonClicked() function’s code with this one:
// Put the CloudKit private database in a constants let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase // Create zone called, friendsZone let customZone = CKRecordZone(zoneName: "FriendsZone") // Save the friendsZone in the private database privateDatabase.saveRecordZone(customZone, completionHandler: ({returnRecord, error in if error != nil { // Zone creation failed NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Cloud Error\n\(error.localizedDescription)" } } else { // Zone creation succeeded NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "The 'FriendsZone' was successfully created in the private database." } } }))
Now, run the app in the iPhone 6 Simulator. Figure 1 below shows output you’ll see on the simulator’s screen, when you click the “Execute Code” button. Figure 2 shows what the friendsZone you created in the CloudKit’s private database look like.
Figure 1 | Figure 2 |
Add records in the custom zone
Here is the code to add a single record in the custom zone you just created in the CloudKit’s private database.
@IBAction func executeCodeButtonClicked() { // Put the CloudKit private database in a constants let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase // Create a zone name let customZone = CKRecordZone(zoneName: "FriendsZone") // Create a friendRecord var friendRecord = CKRecord(recordType: "Friends", zoneID: customZone.zoneID) // Add two fields in the friendRecord friendRecord.setObject("Barbara", forKey: "firstName") friendRecord.setObject("Walter", forKey: "lastName") // Save the friendRecord in the private database's friendsZone privateDatabase.saveRecord(friendRecord, completionHandler: { returnRecord, error in if error != nil { // On the main thread display the error in the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Cloud Error\n\(error.localizedDescription)" } } else { // On the main thread display a success message in the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Record saved successfully in the custom zone called, FriendsZone." } } }) }
Run the app in the iPhone 6 Simulator and click the Execute Code button. You will see output shown in Figure 3 on the simulator’s screen.
To verify that code you entered in the executeCodeButtonClicked() function worked, go to the CloudKit Dashboard and click the “FriendsZoneode” item in the PRIVATE DATA section. The CloudKit Dashboard will look like this:
To make above message go away so you can see the record you entered in the FriendsZone click the “Add ID Query Index” link.
Now, add a second record in the custom zone by changing code line 12 and 13 to this. Run the app in the iPhone 6 Simulator and click the Execute Code button.
friendRecord.setObject("Hayden", forKey: "firstName") friendRecord.setObject("Clark", forKey: "lastName")
Repeat above steps to add a third record in the custom zone; however, change code line 12 and 13 to this:
friendRecord.setObject("Ralph", forKey: "firstName") friendRecord.setObject("Furgerson", forKey: "lastName")
Now, go to the CloudKit Dashboard and click the “FriendsZone” item in the first column to see all three records you’ve added in the private database’s custom zone.
Display the custom zone records
To display records you entered in the private database’s custom zone, you’ll have to add code shown below in the executeCodeButtonClicked() function.
@IBAction func executeCodeButtonClicked() { // Put the CloudKit private database in a constants let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase // Create a zone name let customZone = CKRecordZone(zoneName: "FriendsZone") // Setup a predicate and a query let predicate = NSPredicate(value: true) let query = CKQuery(recordType: "Friends", predicate: predicate) // Execute the query which fetch all records from the private database's custome zone privateDatabase.performQuery(query, inZoneWithID: customZone.zoneID) { records, error in if error != nil { // The query returned an error, on the main thread, show it in the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = error.localizedDescription } } else { // The query return one or more records, declare an array variable for holding // records fetched from the private database's FriendsZone var names = [String]() // Add each record in the names array for record in records { let fName = record.valueForKey("firstName") as! String let lName = record.valueForKey("lastName") as! String names.append(self.emojiBullet + fName + " " + lName) } // Convert the names array to a string let stringRepresentation = "\n".join(names) // On the main thread dump content of the stringRepresentation variabl in the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = stringRepresentation } } } }
Above code pretty much fetch all records from the private database’s custom zone and display them in the textView, like this:
Edit a single record in the custom zone
Say there are 10 records in the custom zone and you want to edit the one highlighted in Figure 8.
You will have to enter code in the executeCodeButtonClicked() function to perform these tasks:
- Fetch the record you want to edit from the private database’ custom zone.
- Edit the fetched record’s fields.
- Save the edited record back in the private database’s custom zone.
Now, here’s the code to implement the first task:
@IBAction func executeCodeButtonClicked() { let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase var recordToEdit: CKRecord! let customZone = CKRecordZone(zoneName: "FriendsZone") let lastName = "Wenderlich" let predicate = NSPredicate(format: "lastName = %@", lastName) let query = CKQuery(recordType: "Friends", predicate: predicate) privateDatabase.performQuery(query, inZoneWithID: customZone.zoneID) { queryResults, error in if (error != nil) { NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = error.localizedDescription } } else { if queryResults.count > 0 { recordToEdit = queryResults[0] as! CKRecord let dbFirstName = recordToEdit.valueForKey("firstName") as! String let dbLastname = recordToEdit.valueForKey("lastName") as! String // DEBUG CODE NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "The query returned \(queryResults.count) record:\n" + dbFirstName + " " + dbLastname } } else { NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Sorry, no record exists in the database for the last name, \(lastName)." } } } } }
Now, when you run the app in the iPhone 6 Simulator and click the Execute Code button. You will see output shown in the image below, on the simulator’s screen. As you can see; the the “DEBUG CODE” displayed the fetched record’s fields in the textView control, thus letting you know that the CloudKit’s performQuery() function did indeed returned a record for the last name you entered in the lastName variable.
Code Analysis
Let us take some time now to examine the code. The first set of statements setup three constants and a variable. The second set of statements setup a predicate and a query. The final set of statements called the CloudKi’s performQuery() function on the privateDatabase. The function’s job is to query the private database’s custom zone for records that match the last name stored in the lastName constant. If the performQuery() function failed to do its job; it returns an error message and we store it in the error variable. If the function found records that match the lastName, it returns an array of records and we store them in the queryResults array. Notice how the textView was updated on the main thread-see code line 72, 83, and 88.
Now, here is the code to implement the second and third task.
@IBAction func executeCodeButtonClicked() { let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase var recordToEdit: CKRecord! let customZone = CKRecordZone(zoneName: "FriendsZone") let lastName = "Wenderlich" let predicate = NSPredicate(format: "lastName = %@", lastName) let query = CKQuery(recordType: "Friends", predicate: predicate) //Task 1: FETCH THE RECORD FROM THE PRIVATE DATABASE privateDatabase.performQuery(query, inZoneWithID: customZone.zoneID) { queryResults, error in if (error != nil) { NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = error.localizedDescription } } else { if queryResults.count > 0 { recordToEdit = queryResults[0] as! CKRecord //Task 2: UPDATE THE FETCHED RECORD'S FIELDS recordToEdit.setObject("Fiona", forKey: "firstName") recordToEdit.setObject("Edwards", forKey: "lastName") //Task 3: SAVE THE EDITED RECORD BACK IN THE PRIVATE DATABASE privateDatabase.saveRecord(recordToEdit) { returnedRecord, error in if error != nil { NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Record not updated:\n\(error.localizedDescription)" } } else { NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Record updated." } } } } else { NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Sorry, record not found." } } } } }
Run the app in the iPhone 6 Simulator and click the “Execute Code” button. Figure 10 below show output you’ll see on the simulator’s screen. Figure 111 shows what the updated record look like in the CloudKit Dashboard.
Figure 10: iPhone Simulator | Figure 11: CloudKit Dashboard |
Delete a record from the custom zone
Say you wanted to delete the fourth record from the private database’s custom zone.
You’ll have to enter code in the executeCodeButtonClicked() function to perform these tasks:
- Fetch the record you want to delete from the private database’s custom zone
- Delete the fetched record from the private database’s custom zone.
- On the main thread, update the textView.
Now, here’s the code to implement above tasks in the executeCodeButtonClicked() function.
@IBAction func executeCodeButtonClicked() { // Put the CloudKit private database in a constants let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase // Create a custom zone name let customZone = CKRecordZone(zoneName: "FriendsZone") let lastName = "Furgerson" // Setup a predicate and the query that'll fetch the record the user want to delete from the private database's custom zone let predicate = NSPredicate(format: "(lastName == %@)", lastName) let query = CKQuery(recordType: "Friends", predicate: predicate) // Execute the query which fetch one or more records from private database's custom zone privateDatabase.performQuery(query, inZoneWithID: customZone.zoneID) { records, error in if error != nil { // The query returned an error, on the main thread, show it in the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = error.localizedDescription } } else { // The query return one or more records if records.count > 0 { // Put the first record in a constant let recordToDelete = records.first as! CKRecord // Delete the record from the private database's custom zone privateDatabase.deleteRecordWithID(recordToDelete.recordID) { results, error in if error != nil { // Record deletion failed. On the main thread, update the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Record Deletion Error:\n\(error.localizedDescription)" } } else { // Record deletion was successful. On the main thread, update the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "The record was deleted from the private database." } } } } } } }
When you finish entering above code in the executeCodeButtonClicked() function, run the app in the iPhone Simulator, and click the Execute Code button. The image below show output you’ll see on the simulator’s screen and the CloudKit Dashboard.
Figure 13 |
Delete all records in the custom zone
Currently there are nine records in the private database’s custom zone. To delete all of them, you’ll have to entered code in the ExecuteCodeButtonClicked() function to perform these tasks:
- Show the user an alert view, prompting her if she want to delete all records from the private database’s custom zone.
- If the user click the Yes button on the alert view, delete all records from the custom zone, and remove all records from the friendsList array.
- If the user click the No button on the alert view, do nothing.
All righty then, here is the code to implement above tasks.
@IBAction func executeCodeButtonClicked() { let controller = UIAlertController(title: "Warning!", message:"Are you sure you want to permanently delete all friends records from the database?", preferredStyle: .Alert) controller.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction!) in self.deleteAllCustomZoneRecords() self.friendsList.removeAll(keepCapacity: false) })) controller.addAction(UIAlertAction(title: "No", style: .Default, handler: nil)) presentViewController(controller, animated: true, completion: nil) }
Above code create and display an alert view with two buttons. On the iPhone 6 Simulator, it look like this:
Figure 14 |
On code line 06, we called a user defined function that delete all records from the private database’s custom zone. Here is the code to implement it in the MainViewController.swift file.
func deleteAllCustomZoneRecords() { // Put the CloudKit private database in a constants let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase // Create a custom zone name let customZone = CKRecordZone(zoneName: "FriendsZone") // Setup a predicate and query that'll fetch all records from the private database's custom zone let predicate = NSPredicate(value: true) let query = CKQuery(recordType: "Friends", predicate: predicate) // Execute the query privateDatabase.performQuery(query, inZoneWithID: customZone.zoneID) { allRecords, error in if error != nil { // The query returned an error. On the main thread show it in the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Cloud error\n\(error.localizedDescription)" } } else { // The query return one or more records if allRecords.count > 0 { // Delete all of them from the private database's custom zone for recordToDelete in allRecords { privateDatabase.deleteRecordWithID(recordToDelete.recordID, completionHandler: { record, error in if error != nil { // Record deletion failed; on the main thread, update the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Record deletion error\n\(error.localizedDescription)" } } else { // Record deletion was successful; on the main thread, update the textView NSOperationQueue.mainQueue().addOperationWithBlock { self.textView.text = "Records deleted successfully." } } }) } } } } }
Run the app on the iPhone 6 Simulator and click the “Execute Code” button. You will see the alert view on the sim’s screen-see Figure 12 above. Click the “No” button and the alert view will disappear. Now, check the custom zone in the CloudKit Dashboard and you’ll see that the records are still there. Run the app again on the iPhone 6 Simulator and click the “Execute Code” button. You will see the alert view again. This time click the “Yes” button. The Alert View will disappear and you will the message shown in Figure 15 on the sim’s screen.
Figure 15 |
Go to the CloudKit Dashboard to verify that the deleteAllCustomZoneRecords() function deleted all records from the private database’s custom zone. As you can see in the image below, all records were deleted from the private database’s FriendsZone.
Figure 16 |