CHAPTER 8

image

CloudKit

Chapter 7 discussed storing preferences both locally on the AppleTV and in the cloud using NSUserDefaults and NSUbiquitousKeyValueStore. This method works great for storing small pieces of information, but what happens when the app needs to store a significant amount of information? What happens when an app needs to search or sort this type of information? This is where CloudKit comes in. CloudKit is a framework provided by Apple that allows developers to easily sync databases between different devices.

CloudKit is currently available only for iOS , Mac OS X, and tvOS devices. Apple has provided CloudKit JS. CloudKit JS looks to enable web apps and any other apps that can implement javascript to hook into existing CloudKit databases. This chapter will not cover CloudKit JS since it is not needed on tvOS.

Considerations for Using CloudKit

CloudKit is virtually free! Apple currently provides developers with 10GB of asset storage and 2GB data transfer per month for free with a developer account. They also provide 100MB of database storage and 40 requests per second. All of this storage is provided for free, but that is not the best part. Apple increases all four of the limitations as you add additional users. For example, with 100,000 active users, the asset storage is increased to 25TB. As an app scales even larger, Apple will provide up to 1 petabyte (PB or 1000 terabytes or 1,000,000 gigabytes) of asset storage and 400 requests per second. Most apps will be able to implement CloudKit without paying any monthly fees. Apple provides pricing and limits details on their iCloud for Developers site (https://developer.apple.com/icloud/).

CloudKit data are either stored as public or private. Public data are available to all of those with the app. Private data are available only to specific iCloud accounts. As a developer designs their app, they need to consider what types of data are available to all and which data are account specific.

CloudKit Containers

All CloudKit data are separated into different containers. Containers will hold all of the databases and information for each CloudKit-enabled app. Each app will have its own container. It is, however, possible to share containers with different apps from the same developer. The container ID will match the app’s bundle ID. Apple provides a class called CKContainer for accessing the different containers.

The default app container can be accessed by using the following code:

var myContainer = CKContainer.defaultContainer()

Creating a custom container is easy.

  1. Head to the Apple developer home page (http://developer.apple.com) and log in. Select Member Center image Certificates, Identifiers, & Profiles. Then select Identifiers under iOS Apps. This will bring up a menu on the left-hand side similar to that shown in Figure 8-1. Select iCloud Containers.

    9781484217146_Fig08-01.jpg

    Figure 8-1. Select iCloud Containers

  2. Click the Add button, as shown in Figure 8-2.

    9781484217146_Fig08-02.jpg

    Figure 8-2. Adding a new iCloud Container

  3. You will then be prompted to enter a description and an identifier, as shown in Figure 8-3. The description is only used to display what is stored in the container. The identifier is necessary for accessing the container.

    9781484217146_Fig08-03.jpg

    Figure 8-3. Creating a new Container

  4. Enter CloudKit Demo2 for the description and enter a unique string for the identifier. Identifiers tend to follow the pattern of iCloud.com.companyID.containerName. The identifier will be used to access the container in the different apps. Click Continue and you will see a screen similar to that shown in Figure 8-4.

    9781484217146_Fig08-04.jpg

    Figure 8-4. Finished Creating the CloudKit Container

Click the Register button and the container is now ready to be used. To access this container, use the following code:

myContainer = CKContainer.init("iCloud.com.inno.cloudkit2")

Databases

The container ID will need to be replaced with whatever you used to register your container in the previous step. Each container will contain a public and a private database. The public database will contain information and assets that are shared among all of the instances of the database. All users will have read and write access to the public database through your app.

Image Note  A developer needs to be careful when dealing with the public database. If one user is able to delete items from it, all other users will be affected.

The private database is only accessible to the current user. The user will have to enter their username and password, but then they will have read and write access to that database.

CloudKit Databases

Apple has provided the CKDatabase class for accessing the databases. Once a container has been connected, it is easy to access the public and private databases. Use the following code to access the private database:

var myContainer = CKContainer.defaultContainer()
  var publicDatabase: CKDatabase = myContainer.publicCloudDatabase
var privateDatabase: CKDatabase = myContainer.privateCloudDatabase

Database Records

A CKRecord is used to store data in your database. Each CKRecord has different pieces of data stored as key-value pairs. Records should be separated into distinct tables or record types for each type of data. For example, for a bookstore, it would make sense to have a record type of Book and a separate record type of Author. Each record type will have a name and fields associated with it. Table 8-1 from Apple’s documentation (https://developer.apple.com/library/tvos/documentation/DataManagement/Conceptual/CloudKitQuickStart/CreatingaSchemabySavingRecords/CreatingaSchemabySavingRecords.html) shows the possible field types for a record.

Table 8-1. Possible Field Types for a Record

Field Type

Class

Description

Asset

CKAsset

A large file that is associated with a record but stored separately

Bytes

NSData

A wrapper for byte buffers that is stored with the record

Date/Time

NSDate

A single point in time

Double

NSNumber

A double

Int(64)

NSNumber

An integer

Location

CLLocation

A geographical coordinate and altitude

Reference

CKReference

A relationship from one object to another

String

NSString

An immutable text string

List

NSArray

Arrays of any of the above field types

Creating a record is a fairly easy process. The following code would accomplish this:

let newBook = CKRecord(recordType: "Book")

This creates a new constant called newBook that is of the type Book. Now, you are able to set values of fields on this newBook constant:

newBook.setValue("The Hobbit", forKey: "title")
newBook.setValue("J. R. R. Tolkien", forKey: "author")

This code sets the title of the book equal to "The Hobbit". The author of the book was also set. Now that the CKRecord of the book is all set, it can be saved to the database. You would save this record to the public database in this case with the following code:

27 CKContainer.defaultContainer().publicCloudDatabase ().saveRecord(newBook, completionHandler: { (record: CKRecord?, error: NSError?) in
28
29         if error != nil {
30             print("There was an error")
31
32         } else {
33             print("Record Saved Successfully")
34         }
35     })

Line 27 tells the public database of the default container to save this record. It also passes in a completion handler, which is a method that will run when the first method is complete. The completion method, in this case, merely tells you if there was an issue saving the record.

Example CloudKit App

Let’s create a CloudKit tvOS app. Launch Xcode and select Create a new Xcode project. You will be prompted with the type of project. Select tvOS on the left-hand side and select Single View Application, as shown in Figure 8-5. Click Next.

9781484217146_Fig08-05.jpg

Figure 8-5. Create a Single View tvOS application

On the next screen, fill in the name of your application. We are using CKBookStore for the name. The Organization Name and Organization Identifier should already be filled in. Make sure the language selected is Swift. None of the check boxes need to be checked (see Figure 8-6). Then click Next.

9781484217146_Fig08-06.jpg

Figure 8-6. Naming the project

You will be prompted to save your project. Select a location you can easily access. Once Xcode opens the project, you will see something similar to Figure 8-7.

9781484217146_Fig08-07.jpg

Figure 8-7. New project screen

By default, the project is selected in the Project Navigator on the left, and the active target will be selected in the targets list. This allows you to change settings and enable CloudKit as the active target. Select Capabilities from the Targets menu, as shown in Figure 8-8.

9781484217146_Fig08-08.jpg

Figure 8-8. Select Capabilities in the Targets menu

Expand the arrow next to iCloud. Toggle the switch to On. If your user account belongs to multiple teams, Xcode will prompt you to select the team to connect with this application. Make sure CloudKit is checked under the Services heading. Your screen should now look like Figure 8-9. The Container IDs

9781484217146_Fig08-09.jpg

Figure 8-9. Successfully added CloudKit to the app

For this app, the default container will be used. If additional containers are available for this app, they will be displayed and are able to be selected on this screen. Apple also provides a link to the CloudKit Dashboard. This is a web interface Apple provides developers for administering CloudKit databases. Click the CloudKit Dashboard.

Image Note  The CloudKit Dashboard works best in Safari.

After entering your developer credentials, you should see a screen similar to Figure 8-10.

9781484217146_Fig08-10.jpg

Figure 8-10. CloudKit Dashboard

You won’t be doing anything in the Dashboard for now, but it is useful for managing record types, queries, and records. You will be using it later in the chapter.

For now, let’s go back to the app and click the AppDelegate.swift file in the Project Navigator. You need to add a method to create book records in CloudKit. Under the import UIKit line, add the following line:

import CloudKit

Now add the following method at the end of the file, but inside the closing brace:

45 func setupBooks() {
46
47         let newBook = CKRecord(recordType: "Books")
48         newBook["title"] = "The Hobbit"
49         newBook["author"] = "J. R. R. Tolkien"
50
51         CKContainer.defaultContainer().publicCloudDatabase.saveRecord(newBook) { (record: CKRecord?, error: NSError?) -> Void in
52             print("Done")
53             if(error != nil) {
54                 print("error")
55                 print(error.debugDescription)
56             }
57         }
58     }

Line 45 creates the method called setupBooks. Line 47 creates a new CKRecord of the type Book. Lines 48 and 49 add a title and an author to this book. Line 51 is a little more complicated. It starts by telling the default container to tell its public database to save this record. You then pass in a block method to be executed when the save is either completed or fails. You are passing two parameters. The first one is the record you tried to save and the second one is the error, if any. Line 53 checks to see if there was an error and displays the information about it.

Image Note  If the save function fails, many times it is because the user is not logged into their iCloud account on the device.

To call this method, add the following line to the application didFinishLaunchingWithOptions at the beginning of the file:

setupBooks()

This will add a book to your cloud every time the app is launched. This is a good way to test things out, but you will definitely want to change this in the real world. Once done, your AppDelegate.swift file should look like the one shown in Figure 8-11.

9781484217146_Fig08-11.jpg

Figure 8-11. Finished AppDelegate

Run your app and see if the record was successfully saved. It should be. Open the Console log in Xcode to verify the word “Done.” If you do see an error in the log, it is likely the user will need to log into iCloud on the device.

Now that you have saved this book, you need to work on getting all of the book records from the cloud. You will retrieve the cloud information in the ViewController.swift. Click ViewController.swift. Add the import CloudKit line at the top of the file like we did in the AppDelegate.swift file. In the viewDidLoad method, add the following code to the bottom of the method:

20         let myPredicate: NSPredicate = NSPredicate(value: true)
21         let myQuery: CKQuery = CKQuery(recordType: "Books", predicate: myPredicate)
22
23         CKContainer.defaultContainer().publicCloudDatabase.performQuery(myQuery, inZoneWithID: nil) {
24             results, error in
25             if error != nil {
26                 print("Error")
27                 print(error.debugDescription)
28             } else {
29                 print(results)
30             }
31         }

Line 20 creates an NSPredicate, which is used to create a search query. NSPredicates are also used with Core Data. They are a powerful way to query. The NSPredicate only queries the records where value=true, and this is how you query all of the records. True is always true, so this will create an NSPredicate to return all of the records.

Line 21 creates a CKQuery by passing in the record type and the NSPredicate you created in the previous line. A CKQuery can also have an NSSortDescriptor. This allows you to sort the data you are retrieving back from CloudKit.

Line 23 tells the public database to perform the query. It is possible to segregate your records into different zones. That is beyond the scope of this book, so here just send in nil to the zone identifier parameter.

Lines 24 to 33 are the block methods to be executed once the query is complete. You can now check to see if there is an error. If something failed, it will then display the error in the log. If there is no error, you can print the records you received into the log. Once complete, your code should look like that shown in Figure 8-12.

9781484217146_Fig08-12.jpg

Figure 8-12. Finished viewDidLoad

If you now run this app as it stands, you will receive an error. You now need to go to the CloudKit Dashboard located at https://icloud.developer.apple.com/dashboard/.

One the left-hand side, click Record Types, then Books, as shown in Figure 8-13. The number of public records will change depending on the number of times you have run the app.

9781484217146_Fig08-13.jpg

Figure 8-13. Selecting Books Record Type

You will now see a screen similar to that shown in Figure 8-14.

9781484217146_Fig08-14.jpg

Figure 8-14. Books details

Click the downward arrow underneath Metadata Indexes and check the box next to Record ID, as shown in Figure 8-15. This allows your application to access these metadata as part of a query. You will notice, Apple will inform you of the cost of selecting that index. It will add 5% to your storage requirements. This is fine in this case, but when designing for large CloudKit applications, size will need to be considered.

9781484217146_Fig08-15.jpg

Figure 8-15. Creating a Record ID index

Now click the Save button at the bottom right corner of the screen. Launch your app and you should receive a log similar to that shown in Figure 8-16. There will be one line for each time you called setupBooks().

9781484217146_Fig08-16.jpg

Figure 8-16. Retrieving records from CloudKit

Summary

In this chapter you learned about CloudKit and the basic objects required to access CloudKit. You learned about the CloudKit Developer Console and how to view records and record types in a web browser. You also created an app to save CloudKit records and retrieve them.

This book as shown you how to begin development for the new AppleTV. We have shown some of the familiar iOS controls and classes and also highlighted some of the ones that are different for tvOS. Due to the tvOS lack of local storage, we also spent time demonstrating how to store and retrieve data from iCloud.

Exercises

  1. Add more books to your cloud storage.
  2. Create a new record type in CloudKit or maybe create an Author type.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset