CloudKit Quick Start Contents About This Document 5 See Also 6 Enabling CloudKit in Your App 7 About Containers and Databases 7 Setup 8 Enable iCloud and Select CloudKit 9 Access CloudKit Dashboard 10 Share Containers Between Apps 11 Add Containers to an App 11 Create Custom Containers 13 Verify Your Steps 14 Create an iCloud Account for Development 15 Recap 15 Creating a Database Schema by Saving Records 16 About Designing Your Schema 16 Separate Data into Record Types 16 Decide on Names for Your Records 18 Create Records 18 Save Records 19 Enter iCloud Credentials Before Running Your App 20 Alert the User to Enter iCloud Credentials 22 Run Your App 22 Verify Your Steps 22 View Record Types by Using CloudKit Dashboard 23 Enable Metadata ID Indexes 24 View Records Using CloudKit Dashboard 24 Recap 25 Using CloudKit Dashboard to Manage Databases 26 About the Development and Production Environments 26 Select Your Container 27 Reset the Development Environment 28 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 2 Contents Create and Delete Record Types 28 Add, Modify, and Delete Records 30 Search Records 31 Sort Records 32 Recap 33 Fetching Records 34 Fetch Records by Identifier 34 Fetch and Modify Records 34 Query for Records Using Predicates 35 Recap 36 Using Asset and Location Fields 38 Store Large Files in CloudKit 38 Verify Your Steps 39 Add Location Fields 39 Verify Your Steps 40 Fetch Records by Location 40 Recap 42 Adding Reference Fields 43 About Modeling Relationships in Your Schema 43 Create Reference Fields 45 Verify Your Steps 46 Fetch Records with Reference Fields 47 Resolve One-To-One Relationships 47 Resolve One-To-Many Relationships 48 Batch Operations to Save and Fetch Multiple Records 49 Specify Ownership to Automatically Delete Related Records 51 Subscribing to Record Changes 52 Save Subscriptions to the Database 52 Verify Your Steps 54 Register for Push Notifications 55 Handle Push Notifications in Code 56 Test Subscriptions 57 Recap 57 Testing Your CloudKit App 58 Distribute Your iOS App Using Ad Hoc Provisioning 58 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 3 Contents Distribute Your Mac App Using the Team Provisioning Profile 60 Recap 60 Deploying the Schema 62 Deploy the Development Schema to Production 62 Verify Your Steps 63 Promote the Development Schema Changes to Production 65 Disable Unused Indexes 65 Assign Roles to Other Team Members 67 Recap 68 Document Revision History 69 Glossary 70 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 4 About This Document Important: This is a preliminary document for an API or technology in development. Apple is supplying this information to help you plan for the adoption of the technologies and programming interfaces described herein for use on Apple-branded products. This information is subject to change, and software implemented according to this document should be tested with final operating system software and final documentation. Newer versions of this document may be provided with future betas of the API or technology. This document gets you started creating a CloudKit app that stores structured app and user data in iCloud. Using CloudKit, instances of your app—launched by different users on different devices—have access to the records stored in the app’s database. Use CloudKit if you have model objects that you want to persist and share between multiple apps running on multiple devices. These model objects are stored as records in the database and can be provided by you or authored by the user. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 5 About This Document See Also You’ll learn how to: ● Enable CloudKit in your Xcode project and create a schema programmatically or with CloudKit Dashboard ● Fetch records and subscribe to changes in your code ● Use field types that are optimized for large data files and location data ● Subscribe to record changes to improve performance ● Test your CloudKit app on multiple devices before uploading it to the App Store or Mac App Store ● Deploy the schema to production and keep it current with each release of your app See Glossary (page 70) for the definition of database terms used in this book. See Also The following WWDC sessions provide more CloudKit architecture and API details: ● WWDC 2014: Introducing CloudKit introduces the basic architecture and APIs used to save and fetch records. ● WWDC 2014: Advanced CloudKit covers topics such as private data, custom record zones, ensuring data integrity, and effectively modeling your data. The following documents provide more information about related topics: ● Designing for CloudKit in iCloud Design Guide provides an overview of CloudKit. ● App Distribution Quick Start teaches you how to provision your app for development and run your iOS app on devices. ● App Distribution Guide contains all the provisioning steps including configuring app services and submitting your app to the App Store or Mac App Store. ● Start Developing iOS Apps Today or Start Developing Mac Apps Today introduces you to Xcode and the steps to create a basic app. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 6 Enabling CloudKit in Your App CloudKit is an app service available only to apps distributed through the App Store and Mac App Store. CloudKit requires additional configuration in your Xcode project. Your app must be provisioned and code signed to access CloudKit. To avoid code signing issues, enable CloudKit using the Capabilities pane in Xcode. There should be no need for you to edit entitlements directly in Xcode or Member Center. About Containers and Databases Multiple apps and users have access to iCloud, but data is segregated and encapsulated in partitions called containers. The containers belonging to your apps cannot be accessed by apps from another developer. However, your apps can share containers. Multiple apps can share the same container, and one app can use multiple containers. There’s one default container per app, but you can create additional custom containers. The identifier for the default container matches the app’s bundle ID. The other container IDs you specify need to be unique across all developer accounts. Containers can’t be deleted. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 7 Enabling CloudKit in Your App Setup An app has access to both a public and private database in each container. The public database is for storing user and app data that is shared between all instances of the app. By default, all users can read the public database, but they need to enter iCloud credentials to write to the public database. There’s a private database for each user of your app, but the app only has access to the private database of the current user. The user has to enter iCloud credentials for the app to read and write to the private database. Setup To perform all the steps in this document, you need: ● A Mac computer with Xcode 6 or later installed ● For the best experience, the latest OS X and Xcode releases installed ● An Xcode project that builds without errors ● Membership in the Apple Developer Program ● Permission to create code signing and provisioning assets in Member Center Verify that you have performed these tasks before you begin using CloudKit. For step-by-step instructions, read App Distribution Quick Start . Task Join the Apple Developer Program. Create an Xcode project that builds and runs. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 8 Enabling CloudKit in Your App Enable iCloud and Select CloudKit Task Add your Apple ID to Accounts preferences. Create your team provisioning profile: For Mac apps, choose Mac App Store as the signing identity. Select your team from the Team pop-up menu. Click Fix Issue. For Mac apps, enable App Sandbox in the Capabilities pane. If you successfully complete the preceding tasks, the error message and Fix Issue button below the Team pop-up menu in the General pane disappears. The screenshot below shows the General pane for an iOS app when the code signing assets are successfully created. To troubleshoot code signing and provisioning, read Troubleshooting in App Distribution Guide . Enable iCloud and Select CloudKit CloudKit is one of three app services provided by iCloud. The other iCloud app services—key-value storage and iCloud documents—also appear in the iCloud settings in Xcode. To use CloudKit, you first enable iCloud and then select the CloudKit service. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 9 Enabling CloudKit in Your App Access CloudKit Dashboard Important: When you select CloudKit, Xcode creates a default container ID based on the bundle ID. Because you can’t delete iCloud containers, verify that your bundle ID is correct in the General pane in Xcode before selecting CloudKit. To change your bundle ID, read Set the Bundle ID in App Distribution Quick Start . To enable iCloud and select CloudKit 1. In the Capabilities pane, select the switch in the iCloud row. Xcode provisions your app to use iCloud. (Key-value storage is enabled by default.) 2. Select the CloudKit checkbox. Xcode creates a default CloudKit container based on the bundle ID and adds the CloudKit framework to your project. Your app can now store data and documents in iCloud. Access CloudKit Dashboard Use CloudKit Dashboard to manage your CloudKit container schema and records. The schema describes the organization of records, fields, and relationships in a database. A record is an instance of a record type. In a relational database, a record type corresponds to a table and a record corresponds to a row in a table. To sign in to CloudKit Dashboard 1. In the iCloud settings in the Capabilities pane, click CloudKit Dashboard. Alternatively, go to https://icloud.developer.apple.com/dashboard. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 10 Enabling CloudKit in Your App Share Containers Between Apps 2. If necessary, enter your Apple ID credentials and click Sign In. All the containers for all the teams you belong to appear in the container pop-up menu in the upper-left corner of the window. The assets for the selected container (named Gallery in the screenshot) are displayed. To sign out, choose Sign Out from the account pop-up menu (named Tom Clark in the screenshot) in the upper-right corner of the window. Share Containers Between Apps Optionally, configure your app to use multiple containers or share a container with other apps. For example, you might use one app internally to create record types and records programmatically to return a database to a known state. This app needs to share the same container as the end-user app you are developing and testing. To do this, you enable the first app to use the default container of the second app or create a custom container that both apps share. iOS and Mac apps can also be configured to share the same containers. Add Containers to an App Select an existing container ID used by another app or create a new one. To add a container to an app 1. In the Capabilities pane under the iCloud settings, select “Specify custom containers.” 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 11 Enabling CloudKit in Your App Share Containers Between Apps When you previously selected the CloudKit service, Xcode created a default container ID for your app that matches the bundle ID. A checkmark appears next to the default container ID. 2. If necessary, click the Refresh button below the Containers table to download containers from Member Center that are used by other apps. 3. In the row of the container ID you want to add, select the checkbox. Xcode updates the list of container IDs in the entitlements file. The screenshot below shows the Curator and Gallery app sharing the Gallery app’s default container. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 12 Enabling CloudKit in Your App Share Containers Between Apps Create Custom Containers Alternatively, create a custom container shared by multiple apps. Important: Only team admins can create containers. If you are an individual, you are the team agent (with admin privileges) for your one person team. To create a custom container 1. If "Use default container” is selected, select “Specify custom containers.” 2. Click the Add button (+) at the bottom of the table. 3. In the dialog that appears, enter an identifier for the container you want to add. Warning: You can’t delete a container ID, so choose the container ID carefully. A container ID begins with iCloud. followed by a string in reverse DNS notation, as in the iCloud.com.example.gkumar1.SharedGallery container ID. 4. Click OK. Xcode adds the new container ID to the Xcode project entitlements file and to Member Center. If you want to share the new container ID with another app, add the container to the app, as described in Add Containers to an App (page 11). 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 13 Enabling CloudKit in Your App Share Containers Between Apps Verify Your Steps You can view all the container IDs for your team in the iCloud settings or Member Center. In Member Center, you can also add containers and edit the name of containers. To view container IDs in Member Center 1. In Member Center, select Certificates, Identifiers & Profiles. 2. Under Identifiers, select iCloud Containers. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 14 Enabling CloudKit in Your App Create an iCloud Account for Development Create an iCloud Account for Development You’ll need an iCloud account to save records to a CloudKit container. You’ll enter the credentials for this iCloud account on the device that you run your app. If you don’t have an iCloud account, create one that you can use during development. On your Mac, launch System Preferences and click iCloud. Click Create Apple ID under the Apple ID text field and follow the instructions. Recap In this chapter, you learned how to: ● Enable CloudKit in your Xcode project, which creates your app’s default container. ● Access CloudKit Dashboard to view the container’s schema and records. ● Create an iCloud account to use for development. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 15 Creating a Database Schema by Saving Records During development, it’s easy to create a schema using CloudKit APIs. When you save record objects to a database, the associated record types and their fields are automatically created for you. This feature is called just-in-time schema and is available only when you use the development environment which is not accessible by apps sold on the store. For example, during development you can populate a CloudKit database with test records stored in a property list. This chapter introduces some CloudKit APIs and contains code fragments. Add #import <CloudKit/CloudKit.h> at the top of each implementation file that uses CloudKit classes and methods. Read CloudKit Framework Reference for details on CloudKit APIs. About Designing Your Schema Design the CloudKit schema to store the portions of your app’s object model that you want to persist. If you are implementing an app from scratch, use the Model-View-Controller design pattern to separate the user interface views from the model objects. Then design a schema that efficiently stores the portions of the object model you want to store in iCloud. Separate Data into Record Types A CloudKit schema consists of one or more record types that have a name, fields, and other metadata. The field types are similar to the allowable property list types with some additions. Use a specialized Asset type for bulk data that is stored separately from records, a Location type for efficiently querying geographical coordinates, and a Reference type to represent one-to-one and one-to-many relationships among records. The maximum size of a record is 1MB, so use the Asset type (not the Bytes type) for large data. The table shows possible field types and their equivalent classes. 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 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 16 Creating a Database Schema by Saving Records About Designing Your Schema Field Type Class Description 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 Design record types using these fields to store your app’s persistent data. For example, this sketch of a master-detail user interface displays a collection of artwork titles in the master interface (column on the left) and properties of the selected artwork in the detail interface (area on the right). The underlying object model consists of an Artwork and Artist class where Artwork has a to-one relationship to Artist. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 17 Creating a Database Schema by Saving Records Create Records There’s a one-to-one mapping between the objects in this object model and the record types Artwork and Artist. The artist field of the Artwork record type is a Reference type with a reference to an Artist record. The image field is an Asset type containing a URL, and the location field is a Location type with longitude and latitude properties. All other fields in Artwork and Artist are simple String and Date types. Decide on Names for Your Records Decide on a heuristic for creating unique names for your records. The record name coupled with a record zone (a partition of a database) is the record identifier that represents the location of a record in a database. The record name can be a foreign key used by another data source or a combination of strings that makes it unique within a record zone. For example, the record name of an Artwork record can combine an artist’s first and last name with a catalog number, as in the string 115 Chen, Mei. If you create records using CloudKit Dashboard, a unique ID is automatically assigned to the record. Create Records First create a record identifier, an instance of the CKRecordID class, specifying the record name and record zone. Then create a record, an instance of the CKRecord class, passing the record identifier. Set the record’s fields using key-value coding style methods. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 18 Creating a Database Schema by Saving Records Save Records To create a record 1. Create a record ID specifying a unique record name. CKRecordID *artworkRecordID = [[CKRecordID alloc] initWithRecordName:@"115"]; 2. Create a record object. CKRecord *artworkRecord = [[CKRecord alloc] initWithRecordType:@"Artwork" recordID:artworkRecordID]; 3. Set the record’s fields. artworkRecord[@"title" ] = @"MacKerricher State Park"; artworkRecord[@"artist"] = @"Mei Chen"; artworkRecord[@"address"] = @"Fort Bragg, CA"; Save Records First select a database where you will save the records (public, private, or custom) and then save the record. If a record type doesn’t exist for the record, it is created for you. To save a record 1. Get the database in your app’s default container. To get the public database: CKContainer *myContainer = [CKContainer defaultContainer]; CKDatabase *publicDatabase = [myContainer publicCloudDatabase]; To get the private database: CKContainer *myContainer = [CKContainer defaultContainer]; CKDatabase *privateDatabase = [myContainer privateCloudDatabase]; To get a custom container: 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 19 Creating a Database Schema by Saving Records Enter iCloud Credentials Before Running Your App CKContainer *myContainer = [CKContainer containerWithIdentifier:@"iCloud.com.example.ajohnson.GalleryShared"]; To create a custom container shared by multiple apps, read Share Containers Between Apps (page 11). 2. Save the record. [publicDatabase saveRecord:artworkRecord completionHandler:^(CKRecord *artworkRecord, NSError *error){ if (!error) { // Insert successfully saved record code } else { // Insert error handling } }]; If the record type doesn’t exist, CloudKit framework creates it with the fields you set. Before clicking the Run button in Xcode, enter iCloud credentials on the device, as described in the next section. Enter iCloud Credentials Before Running Your App In production, the default permissions allow non-authenticated users to read records in the public database but do not allow them to write records. Therefore, before you run your app and save records to the database, enter an iCloud account in Settings on iOS or System Preferences on a Mac. Also enable iCloud Drive. Later, write the necessary error handling to present a dialog to the user when iCloud credentials are needed, as described in Alert the User to Enter iCloud Credentials (page 22). In development, when you run your app through Xcode on iOS Simulator or an iOS device, you also need to enter iCloud credentials to read records in the public database. To run your app in iOS Simulator, enter the iCloud credentials in iOS Simulator before you select the simulator and click the Run button in Xcode. You need to perform these steps for each iOS Simulator you select in the Scheme pop-up menu in Xcode. To enter iCloud credentials in iOS Simulator 1. Choose Xcode > Open Developer Tool > iOS Simulator. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 20 Creating a Database Schema by Saving Records Enter iCloud Credentials Before Running Your App 2. In iOS Simulator, choose Hardware > Home . 3. Launch the Settings app and click iCloud. 4. Enter an Apple ID and password. 5. Click Sign In. Wait while iOS verifies the iCloud account. 6. To enable iCloud Drive, click the iCloud Drive switch. If the switch doesn’t appear, iCloud Drive is already enabled. For how to create an iCloud account, read Create an iCloud Account for Development (page 15). 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 21 Creating a Database Schema by Saving Records Alert the User to Enter iCloud Credentials Alert the User to Enter iCloud Credentials Before saving records, verify that the user is signed in to their iCloud account. If the user is not signed in, present an alert instructing the user how to enter their iCloud credentials and enable iCloud Drive. [[CKContainer defaultContainer] accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) { if (accountStatus == CKAccountStatusNoAccount) { UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Sign in to iCloud" message:@"Sign in to your iCloud account to write records. On the Home screen, launch Settings, tap iCloud, and enter your Apple ID. Turn iCloud Drive on. If you don't have an iCloud account, tap Create a new Apple ID." preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]]; [self presentViewController:alert animated:YES completion:nil]; } else { // Insert your just-in-time schema code here } }]; To learn about errors that may occur when saving records, read CloudKit Framework Constants Reference . Run Your App In Xcode, run your app to execute the code that saves records and creates the schema in the database. Verify Your Steps Use CloudKit Dashboard to verify that the record types were added to the schema and that the records were added to the database. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 22 Creating a Database Schema by Saving Records Verify Your Steps View Record Types by Using CloudKit Dashboard Verify that the record types have the correct field names and types. To view record types 1. Sign in to CloudKit Dashboard. 2. In the upper-left, choose the container used by your app from the pop-up menu. If only one app is enabled to use CloudKit, the name of its default container appears, not the pop-up menu. Warning: If the container ID doesn’t immediately appear in CloudKit Dashboard, try signing out and signing in to CloudKit Dashboard again. If you just enabled CloudKit, it may take a moment for the container to be created and appear in CloudKit Dashboard. 3. In the left column, click Record Types. The record types you added appear in the second column. 4. In the second column, select a record type. The field names and types appear in the detail area on the right. The Users record type that appears is a reserved system record type that cannot be deleted, but you can add fields to it. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 23 Creating a Database Schema by Saving Records Verify Your Steps Enable Metadata ID Indexes All the metadata indexes for record types created using just-in-time schema are disabled by default. The record ID query index needs to be enabled to view the associated records in CloudKit Dashboard. To enable the record ID query index 1. In the left column, click Record Types and select a record type. 2. Click the index disclosure triangle under Metadata Indexes. 3. Select the Query box in the ID row and click Save. View Records Using CloudKit Dashboard Verify that the records you saved have all the data. To view records 1. In the left column of CloudKit Dashboard, click Default Zone under Public Data or Private Data. Records appear in the second column. (If you don’t use a custom zone, records are stored in the public default zone.) 2. In the second column, click a record. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 24 Creating a Database Schema by Saving Records Recap The record key-value pairs appear in the detail area on the right. Recap In this chapter you learned how to: ● Create your schema by saving records programmatically ● Share container IDs between multiple apps ● Use CloudKit Dashboard to view the record types and records you created 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 25 Using CloudKit Dashboard to Manage Databases You perform many database management tasks using CloudKit Dashboard. For example, you can modify both the schema and records using CloudKit Dashboard. A container’s databases exist in both the development and the production environment. The operations you can perform depend on whether you are in the development or production environment. Go to CloudKit Dashboard, sign in, and click the options in the left column to explore CloudKit Dashboard features. About the Development and Production Environments The development environment is used to create a schema and add records for testing. The production environment is accessed by apps sold on the App Store or Mac App Store. An app in development, can access either the development or the production environment. However, an app sold on the store can access only the production environment. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 26 Using CloudKit Dashboard to Manage Databases Select Your Container In the development environment, CloudKit automatically creates the schema for you based on the records that you save to the database. This feature allows you to iterate and refine your schema without having to explicitly create it. You can also use CloudKit Dashboard to modify the schema and add records. The first time you deploy the schema, the schema is copied to the production environment (records are not copied to the production environment). The next time you deploy the schema, the schema is merged with the production schema. To prevent conflicts, you can’t delete fields or record types in a development schema that were previously deployed to the production environment. In the production environment, you can’t change the schema but you can add, modify, and delete records in the public database. When you run your CloudKit app through Xcode, it is automatically configured to use the development environment. When you export your app from Xcode for testing, you specify either the development or production environment. When you submit your app to the store, it is configured to use the production environment. Select Your Container All of the functions in CloudKit Dashboard apply to the currently selected container. Switch the container using the pop-up menu in the upper-left corner. CloudKit Dashboard displays all the containers belonging to all the Apple Developer Program teams you belong to. Be sure to select the container used by the app that you are developing before performing any of the tasks in this chapter. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 27 Using CloudKit Dashboard to Manage Databases Reset the Development Environment Reset the Development Environment If you use just-in-time schema to populate a database with records, as described in Creating a Database Schema by Saving Records (page 16), you can reset the development environment between runs of your app. If you have never deployed your development environment, resetting the development environment deletes all the records and record types. Otherwise, it deletes all the records and returns the schema to the state of the production environment. To reset the development environment 1. In CloudKit Dashboard, click Deployment in the left column. 2. Click Reset Development Environment. 3. In the dialog that appears, read the warning, select the checkbox, and click Reset & Delete Data. Create and Delete Record Types In the development environment, you can create, modify, and delete record types using CloudKit Dashboard. To create a record type 1. In CloudKit Dashboard, click Record Types under Schema. The schema’s record types appear in the second column. 2. Click the Add button (+) in the upper-left corner of the detail area. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 28 Using CloudKit Dashboard to Manage Databases Create and Delete Record Types 3. Enter a name in the New Record Type field. 4. Enter a field name, and select a field type from the pop-up menu. 5. To add a field, click Add Field, enter a field name, and select a field type from the pop-up menu. 6. To delete a field, click the Delete button (x) in the row of the field. The Delete button appears when you hover over the row. If the field is deployed, the Delete button is disabled. 7. Click Save. You can delete a record type only in the development environment and only when that record type is not deployed. When you delete a record type, all its associated records are deleted from the database too. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 29 Using CloudKit Dashboard to Manage Databases Add, Modify, and Delete Records To delete a record type 1. In CloudKit Dashboard, click Record Types under Schema. The schema’s record types appear in the second column. 2. Select the record type you want to delete. 3. Click the trash icon in the upper-left corner of the detail area. If the record type is deployed, the trash icon is disabled. 4. In the dialog that appears, click Delete. Add, Modify, and Delete Records In the development and production environment, you can add, modify, and delete records in public databases using CloudKit Dashboard. To create a record 1. In CloudKit Dashboard under Public Data or Private Data, click a zone. For example, in the public database, click Default Zone. (The User Records zone contains records of type Users created by CloudKit.) 2. In the second column, choose the record type from the pop-up menu in the upper-left corner. 3. In the detail area, click the Add button (+). CloudKit Dashboard assigns a random UUID as the record ID. 4. Enter values in the text fields. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 30 Using CloudKit Dashboard to Manage Databases Search Records 5. For Date/Time types, enter the calendar date and the time values in separate text fields. 6. For Location types, enter the latitude and longitude in separate text fields. The latitude ranges from -90 and 90, and longitude ranges from -180 to 180. 7. For Asset values, drag the file to the box or click “Or Choose File” to upload the file. 8. Click Save. To view, modify, or delete a record 1. In CloudKit Dashboard, click the zone under Public Data or Private Data. 2. In the second column, choose the record type form the pop-up menu. 3. Select the record you want to view, edit, or delete. The record fields appear in the detail area. 4. To edit a record, enter new values in the text fields and click Save. 5. To delete a record, click the trash icon in the upper-left corner of the detail area and in the dialog that appears, click Delete. Search Records In the development and production environment, you can search for records that have string fields. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 31 Using CloudKit Dashboard to Manage Databases Sort Records To search for records 1. In the left column of CloudKit Dashboard, click Default Zone under Public Data or Private Data. Records appear in the second column. 2. In the second column, click the search icon. A search field appears. 3. Enter text in the search field. CloudKit Dashboard sorts the records by the field values. If the record type doesn’t have a searchable field, “… doesn’t have a searchable field.” appears below the search field. Sort Records In the development and production environment, you can sort records by field. To search for records 1. In the left column of CloudKit Dashboard, click Default Zone under Public Data or Private Data. Records appear in the second column. 2. In the second column, click a record. 3. From the “Sort by address” pop-up menu, select a field. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 32 Using CloudKit Dashboard to Manage Databases Recap CloudKit Dashboard sorts the records by the field values. Recap This chapter introduces you to managing your databases with CloudKit Dashboard. You learned how to: ● Reset the development environment to a known state ● Create and delete record types ● Create and edit records 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 33 Fetching Records After you save records to the database, you can retrieve them using different mechanisms. Fetch individual records by record ID, or query for many records using a predicate. (A predicate defines logical conditions for searching for records.) Typically, you fetch a subset of records to display to the user at launch and then subscribe to changes that interest the user. If you use the Location field type, you can also fetch records within a geographical region, as described in Fetch Records by Location (page 40). Fetch Records by Identifier If you know the record IDs for the records you want to fetch, then you can fetch by individual record ID. For example, this code fragment fetches a record named 115. CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase]; CKRecordID *artworkRecordID = [[CKRecordID alloc] initWithRecordName:@"115"]; [publicDatabase fetchRecordWithID:artworkRecordID completionHandler:^(CKRecord *artworkRecord, NSError *error) { if (error) { // Error handling for failed fetch from public database } else { // Display the fetched record } }]; Fetch and Modify Records You can fetch, modify, and save changes you make to individual records. This code fragment fetches an Artwork record, changes its date field value, and saves it to the database. // Fetch the record from the database 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 34 Fetching Records Query for Records Using Predicates CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase]; CKRecordID *artworkRecordID = [[CKRecordID alloc] initWithRecordName:@"115"]; [publicDatabase fetchRecordWithID:artworkRecordID completionHandler:^(CKRecord *artworkRecord, NSError *error) { if (error) { // Error handling for failed fetch from public database } else { // Modify the record and save it to the database NSDate *date = artworkRecord[@"date"]; artworkRecord[@"date"] = [date dateByAddingTimeInterval:30.0 * 60.0]; [publicDatabase saveRecord:artworkRecord completionHandler:^(CKRecord *savedRecord, NSError *saveError) { // Error handling for failed save to public database }]; } }]; Query for Records Using Predicates If you have many records and store large files in iCloud, it is unlikely that you want to store all the records locally on a device. Instead you fetch a slice of the data using a query. A query combines a record type, a predicate, and a sort descriptor where the predicate contains fields that are indexed. You build a query in code using a CKQuery object. For example, this code fragment fetches all artwork with the specified title. CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"title = %@", @"Santa Cruz Mountains"]; CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Artwork" predicate:predicate]; [publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) { if (error) { // Error handling for failed fetch from public database } else { 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 35 Fetching Records Recap // Display the fetched records } }]; In the Gallery app, the artwork with the specified title is fetched. Recap In this chapter you learned how to: ● Fetch records by identifier ● Fetch, modify, and save individual records 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 36 Fetching Records Recap ● Fetch multiple records using a query and predicate 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 37 Using Asset and Location Fields CloudKit provides field types specifically for storing large data files and for fetching records by location. Use these data types to leverage the performance improvements that CloudKit provides for this type of data. You can also fetch records by location. For example, display records on a map in a user-defined region. Store Large Files in CloudKit You can store large data files in CloudKit using the Asset field type. Assets are owned by the associated record, and CloudKit handles garbage collection for you. CloudKit also efficiently uploads and downloads assets. In code, the Asset field type is represented by a CKAsset object. This code fragment sets an Asset field in an Artwork record to a resource file. // Create a URL to the local file NSURL *resourceURL = [NSURL fileURLWithPath:@"…"]; if (resourceURL){ CKAsset *asset = [[CKAsset alloc] initWithFileURL:resourceURL]; artworkRecord[@"image"] = asset; } When the record is saved, the file is uploaded to iCloud. Add similar code that saves a record type with an Asset field to your app and run it. To save the record, read Save Records (page 19). 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 38 Using Asset and Location Fields Add Location Fields Verify Your Steps To verify that your changes to the schema and records were saved to iCloud, read View Record Types by Using CloudKit Dashboard (page 23) and View Records Using CloudKit Dashboard (page 24). When you view records with an Asset object, CloudKit Dashboard displays the size of the binary data. Optionally, download or remove the binary data from the record. Add Location Fields If your record has an address or other location data, you can save it as a CLLocation object in the record and later fetch records by location. For example, your app might display pins representing the records on a map. This code fragment uses the CLGeocoder class to convert a string address to a location object and stores it in a record. CLGeocoder *geocoder = [CLGeocoder new]; [geocoder geocodeAddressString:artwork[kArtworkAddressKey] completionHandler:^(NSArray *placemark, NSError *error){ if (!error) { if (placemark.count > 0){ CLPlacemark *placement = placemark[0]; artworkRecord[kArtworkLocationKey] = placement.location; } } 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 39 Using Asset and Location Fields Fetch Records by Location else { // insert error handling here } // Save the record to the database }]; Add similar code that saves a record type with an Location field to your app and run it. To save the record, read Save Records (page 19). Verify Your Steps When you view records in CloudKit Dashboard, it displays the longitude and latitude of the location field. Fetch Records by Location Once you have location data in your database, you can fetch records by location using a query containing a record type, a predicate, and a sort descriptor. The Location field specified in the predicate must be indexed for the fetch to work. (Fields are indexed by default.) This code fragment fetches all records whose locations are within 100,000 meters of San Francisco. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 40 Using Asset and Location Fields Fetch Records by Location // Get the public database object CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase]; // Create a predicate to retrieve records within a radius of the user's location CLLocation *fixedLocation = [[CLLocation alloc] initWithLatitude:37.7749300 longitude:-122.4194200]; CGFloat radius = 100000; // meters NSPredicate *predicate = [NSPredicate predicateWithFormat:@"distanceToLocation:fromLocation:(location, %@) < %f", fixedLocation, radius]; // Create a query using the predicate CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Artwork" predicate:predicate]; // Execute the query [publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) { if (error) { // Error handling for failed fetch from public database } else { // Display the fetched records } }]; 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 41 Using Asset and Location Fields Recap In this iOS app, artwork within the specified radius of a fixed location is fetched. To learn more about locations and maps, read Location and Maps Programming Guide . Recap In this chapter you learned how to: ● Add an Asset type to a record type by adding a CKAsset field to a record and saving the record ● Add a Location type to a record type using a CLLocation object ● Fetch objects by location 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 42 Adding Reference Fields Use reference fields in your schema to represent relationships between model objects; for example, to represent hierarchical data or indicate ownership. This chapter describes how to add references to records, to save and fetch records with references, and to specify ownership so related records are automatically deleted. About Modeling Relationships in Your Schema You can use reference field types to represent both one-to-one and one-to-many relationships between your model objects. In your code, a reference field is a CKReference object that encapsulates the record ID for a target record and is added to the source record. To represent a one-to-one relationship in your schema, add a reference field to the source record type. To represent a one-to-many relationship between your model objects, it is more efficient if the reference is from the child record to the parent record—that is, add a reference field to the child record. The child record is the source, and the parent record is the target in this schema. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 43 Adding Reference Fields About Modeling Relationships in Your Schema For example, there’s a one-to-many relationship between the Artist and Artwork models and an inverse one-to-one relationship from the Artwork to Artist model. To represent these relationships in the schema, add a reference field to the corresponding Artwork record type called artist. This reference field will contain the record ID of an Artist record. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 44 Adding Reference Fields Create Reference Fields Similarly, to represent the one-to-many relationship from Artist to Collection in the object model, add a reference field to the Collection record. After you fetch these records, you create the appropriate one-to-one and one-to-many relationships between the model objects. Create Reference Fields During development, save records containing reference fields to generate the schema. Represent a one-to-one relationship in your schema by adding a CKReference object to the source record. To create a reference from one record to another 1. Create or get the record ID for the target record. CKRecordID *artistRecordID = [[CKRecordID alloc] initWithRecordName:@"Mei Chen"]; 2. Create a reference object by passing the target’s record ID as a parameter. CKReference *artistReference = [[CKReference alloc] initWithRecordID:artistRecordID action:CKReferenceActionNone]; 3. Add the reference object to the source record. CKRecord *artworkRecord; … artworkRecord[@"artist"] = artistReference; Save the source record to create the record type, as described in Creating a Database Schema by Saving Records (page 16). If you want to save multiple records containing references between them, save all the records in one operation, as described in Use a Single Operation to Save and Fetch Multiple Records (page 49). CloudKit will ensure that target records are saved before source records. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 45 Adding Reference Fields Create Reference Fields Verify Your Steps In CloudKit Dashboard, view the record types, as described in View Record Types by Using CloudKit Dashboard (page 23). A reference field should appear in the source record type. For example, the artist field in the Artwork record type appears as a Reference field type. View the records, as described in View Records Using CloudKit Dashboard (page 24). The target record name should appear in the source record. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 46 Adding Reference Fields Fetch Records with Reference Fields Fetch Records with Reference Fields Don’t use records (CKRecord objects) as your model objects in the Model-View-Controller design pattern. Instead, create separate model objects from fetched records, especially when the fetched records contain references. Your app is responsible for interpreting the references between records and creating the appropriate relationships between model objects. Target records are not automatically fetched when the source record is fetched. Your app is responsible for fetching target records as needed. There are different ways to fetch target and source records depending on the type of relationship the reference represents. If possible, batch fetches to resolve relationships between model objects, as described in Batch Operations to Save and Fetch Multiple Records (page 49). Resolve One-To-One Relationships For one-to-one relationships, get the reference field from the source record and fetch the associated target record. To fetch the target of a one-to-one relationship 1. Get the reference field. CKRecord *artworkRecord; … CKReference *referenceToArtist = artworkRecord[@"artist"]; 2. Get the target record ID from the reference. CKRecordID *artistRecordID = artistReference.recordID; 3. Fetch the target record. [publicDatabase fetchRecordWithID:artistRecordID completionHandler:^(CKRecord *artistRecord, NSError *error) { if (error) { // Failed to fetch record } else { // Successfully fetched record } 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 47 Adding Reference Fields Fetch Records with Reference Fields }]; Add your code to the fetchRecordWithID:completionHandler: completion handler parameter. For example, add code to create the one-to-one relationship between the corresponding Artwork and Artist objects. Resolve One-To-Many Relationships For one-to-many relationships, fetch all the children of a parent record at once using a predicate. You can fetch all records that have the parent as its target record. To fetch children of a one-to-many relationship 1. Start with the parent record ID (CKRecordID) that you previously fetched and the model object for the parent. For example, create an Artist model object from an Artist record. __block Artist *artist = [[Artist alloc] initWithRecord:artistRecord]; Use __block so that you can access the parent object in the completion handler later. 2. Create a predicate object to fetch the child records. NSPredicate *predicate = [NSPredicate predicateWithFormat:@“artist = %@”, artistRecordID]; In your code, replace artist with the name of the reference field in the child record, and replace artistRecordID with the parent record ID. Note: Possible values for the right-hand expression in the predicate format string parameter include CKRecord, CKRecordID, and CKReference objects. 3. Create a query object specifying the record type to search. CKQuery *query = [[CKQuery alloc] initWithRecordType:@“Artwork” predicate:predicate]; In your code, replace @“Artwork” with the name of the child record type. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 48 Adding Reference Fields Batch Operations to Save and Fetch Multiple Records 4. Perform the fetch. CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase]; [publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) { if (error) { // Failed to fetch children of parent } else { // Create model objects for each child and set the one-to-many relationship from the parent to its children } }]; Add the code to the else statement that creates the corresponding relationships between your model objects. Batch Operations to Save and Fetch Multiple Records It is more efficient to batch saves and fetches of multiple records into a single operation. This is especially important if the record types contain reference fields. You can save new source and target records in one operation using a CKModifyRecordsOperation object. You can create a reference to a target record that doesn’t have an identifier. As long as you save both the source and target records together, CloudKit ensures that in a graph of related CKRecord objects, target records are saved before source records. You can also fetch all the targets of a set of source records in one operation using a CKFetchRecordsOperation object. To fetch multiple records in a single operation 1. Add all the record IDs for the records you want to fetch to an array. For example, to fetch multiple one-to-one relationships, add all the target record IDs of the reference fields to an array. 2. Create the fetch record operation object by passing the array of record IDs as a parameter. CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:fetchRecordIDs]; 3. Optionally, provide a per record completion handler. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 49 Adding Reference Fields Batch Operations to Save and Fetch Multiple Records If you want to save the data from successful individual records, provide a per record completion handler. The completion handler should create a model object for a successfully fetched record, and because the operation may fail, should keep the record ID of a failed fetch. fetchRecordsOperation.perRecordCompletionBlock = ^(CKRecord *record, CKRecordID *recordID, NSError *error) { if (error) { // Retain the record IDs for failed fetches } else { // Create a model object and set any relationships to other models } }; 4. Set the completion handler for the entire operation. fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error) { if (error) { // Failed to fetch all or some of the records } else { // Update all associated views } }; If you save the record IDs in the per record completion handler in step 3, you can attempt to fetch failed records again. 5. Start the operation. fetchRecordsOperation.database = [[CKContainer defaultContainer] publicCloudDatabase]; [fetchRecordsOperation start]; 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 50 Adding Reference Fields Specify Ownership to Automatically Delete Related Records Specify Ownership to Automatically Delete Related Records You can specify whether a source record of a reference is deleted when its target record is deleted. For example, you may want the children of a one-to-many relationship to be deleted when the parent is deleted. The parent record owns the children records. If the children own other records, they will also be deleted, causing a cascade of deletions. You specify the delete action when you create the reference object. To delete a source record when the target is deleted, pass the target record and CKReferenceActionDeleteSelf as the action parameter to the initWithRecord:action: method. In the Gallery sample, specify that artwork belonging to an artist should be deleted when the artist is deleted. CKReference *referenceToArtist = [[CKReference alloc] initWithRecord:artistRecord action:CKReferenceActionDeleteSelf]; artworkRecord[@"artist"] = referenceToArtist; Alternatively, check the DeleteSelf box in CloudKit Dashboard for each record. CloudKit deletes the source record when the first target record of a CKReferenceActionDeleteSelf reference is deleted. If a source record has multiple CKReferenceActionDeleteSelf references, it may be deleted even though some of its other target records exist, leading to an inconsistent data model. Try to design your schema so that each record has no more than one CKReferenceActionDeleteSelf reference. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 51 Subscribing to Record Changes It’s inefficient for your app to repeat a query when the results are mostly the same as the last query. Instead, subscribe to record changes, and let the server run the query in the background. The server will notify your app of changes that interest the user or app. For example, if one user of your app is interested in artwork by a certain artist, your app can be notified when new artwork by that artist is uploaded. Save Subscriptions to the Database In your code, create a subscription object specifying the record type, predicate, and types of changes you want to be notified about. Then save the subscription object to the database. To create and save a subscription 1. Create a predicate object. For example, subscribe to artwork from an artist (where the artist field in the Artwork record type is a Reference type). 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 52 Subscribing to Record Changes Save Subscriptions to the Database CKRecordID *artistRecordID = [[CKRecordID alloc] initWithRecordName:@"Mei Chen"]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"artist = %@", artistRecordID]; Note: Possible values for the right-hand expression in the predicate format string parameter include CKRecord, CKRecordID, and CKReference objects. If you know the record name, you can create a record ID containing just the record name. 2. Create a subscription specifying the record type, predicate, and notification options. CKSubscription *subscription = [[CKSubscription alloc] initWithRecordType:@"Artwork" predicate:predicate options:CKSubscriptionOptionsFiresOnRecordCreation]; The possible values for the options parameter are: CKSubscriptionOptionsFiresOnRecordCreation, CKSubscriptionOptionsFiresOnRecordDeletion, CKSubscriptionOptionsFiresOnRecordUpdate, and CKSubscriptionOptionsFiresOnce. Because the options parameter is a bitmask, you can subscribe to any combination of the type of changes. For example, you can pass CKSubscriptionOptionsFiresOnRecordCreation | CKSubscriptionOptionsFiresOnRecordUpdate as the options: parameter to receive notification of all new data. 3. Create a CloudKit notification object. CKNotificationInfo *notificationInfo = [CKNotificationInfo new]; notificationInfo.alertLocalizationKey = @"New artwork by your favorite artist."; notificationInfo.shouldBadge = YES; To display a localized string to the user, set the notification’s alertLocalizationKey property (not the alertBody property). 4. Set the subscription’s notification object to the new CloudKit notification object. subscription.notificationInfo = notificationInfo; 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 53 Subscribing to Record Changes Save Subscriptions to the Database 5. Save the subscription to the database. CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase]; [publicDatabase saveSubscription:subscription completionHandler:^(CKSubscription *subscription, NSError *error) { if (error) // insert error handling } ]; In Xcode, run your app to save the subscription to the database. Verify Your Steps Verify that the subscription was saved to the schema. In CloudKit Dashboard, the subscription object appears as a subscription type in the schema. 1. In CloudKit Dashboard, choose the container used by your app from the pop-up menu in the upper-left corner. 2. Under Schema, click Subscription Types. The subscriptions appear in the second column. 3. If necessary, select a subscription. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 54 Subscribing to Record Changes Register for Push Notifications The subscription properties appear in the detail area. Register for Push Notifications Saving subscriptions to the database doesn’t automatically configure your app to receive subscription notifications. CloudKit uses the Apple Push Notification service (APNs) (page 70) to send subscription notifications to your app, so your app needs to register for push notifications to receive them. For iOS apps, add this code to the application:didFinishLaunchingWithOptions: protocol method to register for push notifications: // Register for push notifications UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:nil]; [application registerUserNotificationSettings:notificationSettings]; [application registerForRemoteNotifications]; For Mac apps, implement the applicationDidFinishLaunching: protocol method to register for push notifications. For both iOS and Mac apps, optionally implement the application:didRegisterForRemoteNotificationsWithDeviceToken: and application:didFailToRegisterForRemoteNotificationsWithError: methods to take the appropriate action when the app successfully or unsuccessfully registers for push notifications. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 55 Subscribing to Record Changes Handle Push Notifications in Code Note: You don’t need to enable push notifications for the app’s explicit App ID in Member Center to receive subscription notifications. Xcode automatically adds the APNs entitlement to your entitlement file when you enable CloudKit. Handle Push Notifications in Code Next, implement the application:didReceiveRemoteNotification: method to process subscription notifications when they arrive. For iOS apps, implement the UIApplicationDelegate protocol method and for Mac apps, implement the NSApplicationDelegate protocol method. For example, implement this method to update views when records matching your predicate are created, updated, or deleted. 1. Add the application:didReceiveRemoteNotification: protocol method to the app’s delegate. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { } 2. In the application:didReceiveRemoteNotification: method, convert the userInfo parameter to aCKNotification object. CKNotification *cloudKitNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo]; 3. Get the body of the notification. NSString *alertBody = cloudKitNotification.alertBody; 4. Get the new or modified record from the CKQueryNotification object. if (cloudKitNotification.notificationType == CKNotificationTypeQuery) { CKRecordID *recordID = [(CKQueryNotification *)cloudKitNotification recordID]; } 5. Update views or notify the user according to the record changes. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 56 Subscribing to Record Changes Test Subscriptions Test Subscriptions You can initially test subscriptions by running your app through Xcode and using CloudKit Dashboard to create, modify, or delete records, as described in Add, Modify, and Delete Records (page 30). Then fully test subscriptions by running your app on multiple devices. Use one device to make changes and another device to receive the subscription notifications. You use multiple devices because a notification isn’t sent to the same device that originated the notification. For iOS, use an iOS device (not iOS Simulator) to test subscription notifications. Your app successfully registers for push notifications if a dialog that asks the user’s permission for your app to receive notifications. Recap In this chapter you learned how to: ● Subscribe to record changes by using a predicate ● Handle subscription notifications 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 57 Testing Your CloudKit App Give your CloudKit app a real-world test with multiple simultaneous users by running it on multiple devices using different iCloud accounts. Both iOS and Mac apps can be configured to launch outside Xcode on designated test devices. This is the recommended way to initially test your CloudKit app in the development or production environment. Later upload your app to iTunes Connect to test your app using the production environment. Using iTunes Connect, invite internal testers (your team’s iTunes Connect users) or invite external testers (users specifying only their email addresses) to test your app. Testers download your app using the TestFlight app. To learn about distributing your app using TestFlight, read Beta Testing Your iOS App in App Distribution Guide . Distribute Your iOS App Using Ad Hoc Provisioning Ad hoc provisioning allows you to run your app on test iOS devices without needed Xcode. Before exporting your app using an ad hoc provisioning profile, register all the devices you want to use for testing with Member Center. Ad hoc distribution uses the same pool of devices that you register for development, so you are limited to 100 per year. To register multiple test devices, read Registering Devices Using Member Center. When you export your app for ad hoc testing, you choose the development or the production environment. If you have not deployed your schema to production, as described in Deploy the Development Schema to Production (page 62), choose the development environment. To archive and export your app for ad hoc testing 1. Choose an iOS device from the Scheme toolbar menu and click Run. You can’t create an archive of a binary that is built for iOS Simulator. 2. Choose Product > Archive. The Archives organizer appears and displays the new archive. 3. In the Archives organizer, select the archive and click Export. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 58 Testing Your CloudKit App Distribute Your iOS App Using Ad Hoc Provisioning 4. Select “Save for Ad Hoc Deployment,” and click Next. 5. In the dialog that appears, choose a team from the pop-up menu and click Choose. If necessary, Xcode creates a distribution certificate and an ad hoc provisioning profile for you. 6. 7. In the next dialog, choose a container environment from the pop-up menu and click Next. ● Choose Production to access the data in the production environment. ● Choose Development to access the data in the development environment. In the dialog that appears, review the app, its entitlements, and its provisioning profile, and click Export. The name of the ad hoc provisioning profile begins with the text XC Ad Hoc:. 8. Enter a filename and location for the iOS App file, and click Export. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 59 Testing Your CloudKit App Distribute Your Mac App Using the Team Provisioning Profile The file has an .ipa extension. Later, send the iOS App file to testers. Instruct them to install the app on their iOS devices using iTunes, as described in Installing Your App on Test Devices. Distribute Your Mac App Using the Team Provisioning Profile Register all the devices you want to use for testing with Member Center before exporting your app using Xcode. To add multiple test devices to the team provisioning profile, read Registering Devices Using Member Center. After you add devices, regenerate the team provisioning profile by refreshing profiles in Xcode, described in Refreshing Provisioning Profiles in Xcode. To export a code signed app using the team provisioning profile 1. Choose a target from the Scheme toolbar menu, and click Run. 2. Choose Product > Archive. The Archives organizer appears and displays the new archive. 3. In the Archives organizer, select the archive and click Export. 4. Select “Export as a Mac Application.” Xcode embeds the team provisioning profile in the bundle and code signs the app with your development certificate. Later, distribute the app to testers and run on the designated devices. The app will launch only on devices specified in the team provisioning profile. If you can’t launch the app on a designated device because it’s from an unidentified developer, bypass the security settings in OS X to launch the app. To open an app from an unidentified developer 1. In Finder, Control-click the app icon. 2. Click Open. 3. In the Gatekeeper dialog that appears, click Open. Recap In this chapter you learned: 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 60 Testing Your CloudKit App Recap ● How to use the different methods to distribute your CloudKit app for testing ● How to restrict your app to run on designated devices ● For iOS apps, how to select the development or production container for testing 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 61 Deploying the Schema After you finalize your schema and test your app in the development environment, you are ready to deploy the schema to production. Deployment promotes the schema to the production environment, but it doesn’t copy the records in the development environment to the production environment. Therefore, after deployment, populate the production environment with records as needed. Then test your app in the production environment. You can continue making changes to the schema in the development environment, but you are limited to creating record types and adding fields. The next time you deploy the development schema, the changes are merged with the production schema. You must have privileges to edit the production environment to perform the tasks in this chapter. If you are an individual, you are the team admin and have these privileges. Otherwise, ask your team admin to perform these steps for you, or to grant you the Edit Production privilege, as described in Assign Roles to Other Team Members (page 67). When you are ready to submit your app to the store, read Submitting Your App to the Store in App Distribution Guide . Deploy the Development Schema to Production The first time you deploy your app, CloudKit copies the container schema to the production environment. This includes the record types, security roles, and subscription types, but not the records that you created in the development environment. Once you deploy your schema to the production environment, you can’t delete record types and fields that were deployed in the development environment. Warning: To view records in production, enable the ID metadata index for the associated record types before following these steps, described in Enable Metadata ID Indexes (page 24). You can’t change the metadata indexes in the production environment. To deploy a schema to production 1. In CloudKit Dashboard, click Deployment in the left column. 2. Click “Deploy to Production.” 3. If an indexing dialog appears, click Optimize Indexes or Deploy Unused. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 62 Deploying the Schema Deploy the Development Schema to Production CloudKit tracks the index usage in the development, and shows you the estimated cost and size of each index. 4. ● To view the details of the unused indexes, click Unused Index Details. ● To remove the unused indexes before deploying the schema, click Optimize Indexes. ● To keep the indexes, click Deploy Unused. ● To analyze the index usage before continuing, click Cancel and read Disable Unused Indexes (page 65). In the dialog that appears, read the message and click Optimize & Deploy or Deploy Unused Indexes (the button title depends on the option you choose in the previous dialog). Verify Your Steps Verify that the schema was copied to the production environment. To view the production schema and data 1. In the lower-left corner, click the Development button. 2. In the dialog that appears, click Production. The interface changes from the development to production environment. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 63 Deploying the Schema Deploy the Development Schema to Production 3. Click Record Types and select the record type you want to view. 4. Click Default Zone to view public records. To go back to the development environment, in the lower-left corner, click the Production button and click Development. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 64 Deploying the Schema Promote the Development Schema Changes to Production Promote the Development Schema Changes to Production The next time you deploy your schema to production, you can add only record types and fields to the production environment. You can’t delete parts of the schema that are in production. CloudKit Dashboard merges the new record types and fields with the production schema. You have the opportunity to review schema changes before deploying the schema. To promote the development schema to production 1. In the development environment, click Deployment in the left column and review the changes you made to the schema. CloudKit Dashboard displays the changes to record types, subscription types, and security roles. 2. Click “Deploy to Production.” 3. If an indexing dialog appears, click either Optimize Indexes or Deploy Unused. CloudKit tracks the index usage in the development. To see the estimated cost and size of each unused, click Unused Index Details. 4. In the dialog that appears, read the message and click Optimize & Deploy or Deploy Unused Indexes . Disable Unused Indexes Indexes improve the speed of fetching records from the database. CloudKit Dashboard creates indexes for each field in a record type. Depending on the field type, CloudKit will create a sort, query, and search index. This allows you to create queries using any field combination. In production, it’s wasteful to maintain and store indexes for fields that you don’t use in database operations. You can save disk space, in both the public and private databases, by disabling unused indexes. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 65 Deploying the Schema Disable Unused Indexes CloudKit tracks the index usage in both the development and production environments. Before you deploy or promote the development schema, test your app thoroughly to create index metrics that represent real world usage. Tune your database by disabling indexes for fields that you don’t use in sort, query, and search operations. CloudKit Dashboard also estimates the cost of indexes derived from usage metrics. You can disable unused indexes in the development and production environment. To disable or enable an index for a field 1. In the left column, click Record Types. 2. In the second column, select a record type. The field names and types appear in the detail area on the right. The Index column contains checkboxes and the Cost column displays the estimated cost of the index. 3. In the Index column, unselect a box to disable a type of index and select a box to enable a type of index. ● If you don’t use the field in sort descriptors (set using the sortDescriptors method in CKQuery) or sort records by the field in CloudKit Dashboard (described in Sort Records (page 32)), unselect the Sort box. ● If you don’t use the field in queries (CKQuery or CKQueryOperation objects) executed by your app, unselect the Query box. ● If you don’t search for records using the field (described in Search Records (page 31)), unselect the Search box. If you deploy the schema to production, as described in Deploy the Development Schema to Production (page 62), a dialog appears allowing you to disable unused indexes before deployment. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 66 Deploying the Schema Assign Roles to Other Team Members Assign Roles to Other Team Members For company accounts, you can delegate some of the responsibilities of deploying your CloudKit app by changing team member privileges, shown below. Privilege Description Manage Team Can change the privileges of other team members, except the team agent. The team agent always has all privileges. Edit Development Edit Production ● Can edit the development schema by using CloudKit Dashboard. ● Can view records in development. ● Can deploy the development schema to production. ● Can view the production schema. ● Can view and edit records in production. You set the team member privileges separately for each container. The privileges don’t apply to all containers belonging to a team. To grant privileges to team members 1. In CloudKit Dashboard, click Team in the left column. 2. In the row of the team member and Privileges column, select the privileges you want to grant to the team member. If a privilege can’t be changed or you don’t have permission to change it, the checkbox is disabled. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 67 Deploying the Schema Recap Recap In this chapter you learned how to deploy your development schema to the production environment and how to keep it up to date as you continue developing your app. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 68 Document Revision History This table describes the changes to CloudKit Quick Start . Date Notes 2015-06-27 Updated screenshots per the latest CloudKit Dashboard and applied other minor edits throughout. 2015-04-08 Added "Adding Reference Attributes" chapter and revised "Deploying the Schema" per CloudKit Dashboard indexing features. 2014-10-31 New document that introduces app development for CloudKit. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 69 Glossary Apple Push Notification service (APNs) Apple service for propagating information to iOS and OS X devices. public database A database for storing records owned by the app that are shared between users. An iCloud account is not required to read records but is required to write records. CloudKit An app service that stores structured application and user data in iCloud. push notifications A notification from a provider to a device transported by APNs. container A data store containing multiple database used by one or more apps. The default container ID matches the app’s bundle ID. record An instance of a record type that can be created, read, and written to a database. database The portion of a container used to store records. There’s one public database for the app and multiple private databases—one private database for each user. record identifier An identifier for the location of a record in a database. Contains a record name and zone. record name A unique identifier for a record within a given zone. The record name is supplied by the app and can be used as a foreign key in another data source. development environment Databases used to develop your app and evolve the schema that is not accessible by apps sold on the App Store or Mac App Store. record type A template for a set of records that have common fields. field A property of a record type that can be set using a key-value pair. record zone A partition of a database to store records. Each database has a default zone and allows additional custom zones. just-in-time schema Development environment feature that allows an app to create a schema by saving records. relationship A record type field that associates one record to another. predicate An object that defines logical conditions for searching for objects conforming to key-value coding. role Permissions for a group of users to create, read, and write records in the public database. The possible roles are world, authenticated, and creator. private database A database for storing records owned by the current user that are not readable by the app unless the user enters their iCloud credentials on the device. schema A collection of metadata that describes the organization of records, fields, and relationships in a database. In CloudKit, the schema includes record types, security roles, and subscription types. production environment Databases accessed by apps sold on the App Store or Mac App Store. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 70 Glossary subscription A persistent query on the server that triggers notifications when records change. to-many relationship An association between a single record and one or more other records. to-one relationship An association between a single record and another single record. 2015-06-27 | Copyright © 2015 Apple Inc. All Rights Reserved. Apple Confidential Information. 71 Apple Inc. Copyright © 2015 Apple Inc. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, mechanical, electronic, photocopying, recording, or otherwise, without prior written permission of Apple Inc., with the following exceptions: Any person is hereby authorized to store documentation on a single computer or device for personal use only and to print copies of documentation for personal use provided that the documentation contains Apple’s copyright notice. No licenses, express or implied, are granted with respect to any of the technology described in this document. Apple retains all intellectual property rights associated with the technology described in this document. This document is intended to assist application developers to develop applications only for Apple-branded products. Apple Inc. 1 Infinite Loop Cupertino, CA 95014 408-996-1010 Apple, the Apple logo, Finder, iTunes, Mac, OS X, Sand, and Xcode are trademarks of Apple Inc., registered in the U.S. and other countries. iCloud is a service mark of Apple Inc., registered in the U.S. and other countries. App Store and Mac App Store are service marks of Apple Inc. IOS is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license. APPLE MAKES NO WARRANTY OR REPRESENTATION, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS DOCUMENT, ITS QUALITY, ACCURACY, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. AS A RESULT, THIS DOCUMENT IS PROVIDED “AS IS,” AND YOU, THE READER, ARE ASSUMING THE ENTIRE RISK AS TO ITS QUALITY AND ACCURACY. IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES RESULTING FROM ANY DEFECT, ERROR OR INACCURACY IN THIS DOCUMENT, even if advised of the possibility of such damages. Some jurisdictions do not allow the exclusion of implied warranties or liability, so the above exclusion may not apply to you.
© Copyright 2024