CloudKit Save a Record

In this lesson you learn how to save an item (with an image) in the default zone of the CloudKit’s public database. Here is a use case scenario of that task:

cloudkit-figure6-1

caution 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.

Step 1: Add a record type in the CloudKitGuide container

To add an item record type in the CloudKit container load the CloudKit Dashboard in your browser (https://icloud.developer.apple.com/dashboard/). If necessary, sign into the Apple Developer portal. Next, select the CloudKitGuide container from the Container menu.

cloudkit-figure6-2

Next, select “Record Types” in the first panel. At the top of the third panel, click the Add New Record button (+) and enter Item in the record type field. The CloudKit Dashboard should look like this now:

cloudkit-figure6-3

Add these field names and field types for the Item record type.

cloudkit-figure6-4

Click the Metadata Indexes arrow. Configure the pop up menu so it look like the one shown in Figure 4, then click  the Save button.

cloudkit-figure6-5

Now, there are three “Record Types” in the CloudKit database: Friends, Item, and Users.

cloudkit-figure6-6

Step 2: Update the Main View Controller scene and its class file

In order for the user to add details of an item in the view’s controls, configure the Main View Controller scene and its class file to look like this:

cloudkit-figure6-7

Now, when you run the app in the iPhone 6 Simulator, the mainView should look like this:

cloudkit-figure6-8

There are few things I want to point out about the MainViewController class file’s code.

  1. You conformed the class to three delegate protocols
  2. You assigned the CloudKit’s public database to a constant called db
  3. You entered code in three functions to dismiss the keyboard

The Image Selection Code

With that said, you have to enter code in the MainViewController.swift file to make the Choose Image button work. Start by adding entering code shown below in the Choose Image button’s function.

@IBAction func chooseImage(sender: UIButton) {
  itemName.resignFirstResponder()
  let imagePicker = UIImagePickerController()
  imagePicker.delegate = self
  imagePicker.allowsEditing = false
  imagePicker.sourceType = .PhotoLibrary
  imagePicker.modalPresentationStyle = .Popover
  imagePicker.popoverPresentationController?.sourceView = sender
  presentViewController(imagePicker, animated: true, completion: nil)
}

Above code present an image picker view on the sim’s screen. So the user can select a photo from the sim’s Photo Library.

When the user finish picking an image from the sim’s Photo Library, the app should dump it in the imageView control. To perform this task, implement this delegate function of the UIImagePickerController class in the MainViewController.swift file.

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
  var selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
  imageView.image = selectedImage
  dismissViewControllerAnimated(true, completion: nil)
}

Now, before you run the app on the iPhone 6 Simulator or on your real device, grab some photos off the web and add them in the simulator’s Photo Library or your device’s Photo Library. Another option is to click the link below to download photos shown in Figure A below. Figure B shows how to add them in the iPhone 6 Simulator’s Photo Library.

sample-photos

cloudkit-photo-set
Figure A

cloudkit-figure6-9
Figure B

Ok, I assume you’ve added above photos in the iPhone 6 Simulator; so run the app in the simulator. When the mainView appear on the sim’s screen do the following:

  1. Click the “Choose Image” button.
  2. Click the alert view’s OK button.
  3. Select an image from the sim’s Photo Library.
  4. The image picker view will disappear and the image you selected will appear in the main view’s imageView.
cloudkit-figure6-8 cloudkit-figure6-6 cloudkit-figur6-8c cloudkit-figure6-8b

We’ve implement code in the MainViewController class file to enable the user to select a photo from the Sim’s Photo Library and dump it in the image view. Now, you have to add code in the class file to save the item details (item name and image) in the CloudKit’s public database. This will happen when you click the main view’s Save button. So locate its function in the MainViewController.swift file and add this code in it.

@IBAction func save() {
    // Dismiss the keyboard
    itemName.resignFirstResponder()

    if thisApp.userSignedIn == false {
        // The app user's iCloud account is not authenticated
        textView.text = "iCloud is not available.\n\nPlease do the following:\n\n" +
            "1. Sign into your iCloud account\n" +
            "2. Turn on iCloud Drive via the Settings app\n" +
        "3. Relaunch the app"

        // Don't execute code below this if block
        return
    }

    if itemName.text.isEmpty {
        textView.text = "Can't save, the item name is required."
        return
    }

    var photoUrl: NSURL!
    let item = DatabaseHeleper()
    item.itemName = itemName.text
    item.itemPhoto = imageView.image!
    textView.text = "Saving item..."

    // Check to see if the item already exists in the CloudKit's public database
    let predicate = NSPredicate(format: "itemName = %@", itemName.text)
    let query = CKQuery(recordType: "Item", predicate: predicate)

    db.performQuery(query, inZoneWithID: nil, completionHandler: ({results, error in
        if (error != nil) {
            NSOperationQueue.mainQueue().addOperationWithBlock {
                self.textView.text = "Search query error: \(error.localizedDescription)"
            }
        } else {
            if results.count > 0 {
                // The item does exists in the CloudKit public database
                // Update the UI control on the main thread
                NSOperationQueue.mainQueue().addOperationWithBlock {
                    self.textView.text = "That item already exists in the database."
                }
            } else {
                // The item doesn't exist in the CloudKit public database
                // Insert the photo in the app's Documents directory
                photoUrl = item.saveImageInDocumentsDir(self.imageView.image!)

                // Create an id for the CKRecord
                let itemId = item.randomStringWithLength(3)

                // Create an asset object and initialized it
                let photoAsset = CKAsset(fileURL: photoUrl!)

                // Create a CKRecord object
                let itemRecord = CKRecord(recordType: "Item")

                // Set the CKRecord object's properties
                itemRecord.setObject(itemId, forKey: "itemId")
                itemRecord.setObject(self.itemName.text, forKey: "itemName")
                itemRecord.setObject(photoAsset, forKey: "itemImage")

                // Save the CKRecord object in the CloudKit public database
                self.db.saveRecord(itemRecord, completionHandler: {returnedRecord, error in
                    if error != nil {
                        // Update the UI control on the main thread
                        NSOperationQueue.mainQueue().addOperationWithBlock {
                            self.textView.text = "Save item error: \(error.localizedDescription)"
                        }
                    } else {
                        // Update the UI on the main thread
                        NSOperationQueue.mainQueue().addOperationWithBlock {
                            self.textView.text = "Item saved."
                        }
                    }
                })
            }
        }
    }))
}

Yes, I Know that’s a lot of code for such a simple task. However, the comments should help demystify the code’s logic. Go ahead and run the app in the iPhone 6 Simulator. Enter a name in the textField and use the Choose Image button to select a photo from the sim’s Photo Library. When you are done, click the save button and you should see output shown in the Figure A below. If you click the Save button without entering a different item name in the textField; the app will not save the item name and image in the public database. It will simply display the message shown in Figure B. Figure C is the CloudKit Dashboard view of the item you entered in the CloudKit’s public database.

Figure A
Figure A

cloudkit-figure6-10b
Figure B

cloudkit-figure6-11
Figure C

That’s pretty much it. You entered code in the MainViewController.swift file to save an item (with an image) in the default zone of the CloudKit’s public database. Comments are welcomed! 🙂