Page 1
© 2014 Apple Inc. All rights reserved. Redistribution or public display not permitted without written permission from Apple.
#WWDC14
Introducing CloudKitA how-to guide for iCloud for your Apps
Session 208 Olivier Bonnet CloudKit Client Software
Frameworks
Page 2
Introduction
iCloud Drive iCloud Photo Library
iCloud CoreData
? Mystery
Page 3
Introduction
CloudKit
iCloud Drive
iCloud CoreData
iCloud Photo Library
Page 5
Overview
What is CloudKit
Page 6
Overview
What is CloudKit
Enabling CloudKit in your application
Page 7
Overview
What is CloudKit
Enabling CloudKit in your application
Introduction to the API
Page 8
Overview
What is CloudKit
Enabling CloudKit in your application
Introduction to the API
User Accounts
Page 9
Overview
What is CloudKit
Enabling CloudKit in your application
Introduction to the API
User Accounts
When to use CloudKit
Page 10
What is CloudKit?
Page 11
What is CloudKit?
Access to iCloud servers
Page 12
What is CloudKit?
Access to iCloud servers
Supported on OS X and iOS
Page 13
What is CloudKit?
Access to iCloud servers
Supported on OS X and iOS
Uses iCloud accounts
Page 14
What is CloudKit?
Access to iCloud servers
Supported on OS X and iOS
Uses iCloud accounts
Public and private databases
Page 15
What is CloudKit?
Access to iCloud servers
Supported on OS X and iOS
Uses iCloud accounts
Public and private databases
Structured and bulk data
Page 16
What is CloudKit?
Access to iCloud servers
Supported on OS X and iOS
Uses iCloud accounts
Public and private databases
Structured and bulk data
Transport, not local persistence
Page 17
Enabling CloudKit in Your Application
Page 18
Enabling CloudKit in Your Application
Navigate to your application’s Capabilities pane
Page 19
Enable iCloud
Enabling CloudKit in Your Application
Page 20
Enable CloudKit
Enabling CloudKit in Your Application
Page 21
Introducing CloudKit API
!
Paul Seligman CloudKit Client Software
Page 22
Fundamental CloudKit Objects
Page 23
Fundamental CloudKit Objects
Page 24
Fundamental CloudKit Objects
Containers
Page 25
Fundamental CloudKit Objects
Containers
Databases
Page 26
Fundamental CloudKit Objects
Containers
Databases
Records
Page 27
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Page 28
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
Page 29
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Page 30
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Assets
Page 31
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Assets
Page 35
Containers
CKContainer
Page 36
Containers
CKContainer
One container per app
Page 37
Containers
CKContainer
One container per app
Data segregation
Page 38
Containers
CKContainer
One container per app
Data segregation
User encapsulation
Page 39
Containers
CKContainer
One container per app
Data segregation
User encapsulation
Managed by the developer
Page 40
Containers
CKContainer
One container per app
Data segregation
User encapsulation
Managed by the developer• Managed via WWDR portal
Page 41
Containers
CKContainer
One container per app
Data segregation
User encapsulation
Managed by the developer• Managed via WWDR portal
• Unique across all developers
Page 42
Containers
CKContainer
One container per app
Data segregation
User encapsulation
Managed by the developer• Managed via WWDR portal
• Unique across all developers
Can be shared between apps
Page 43
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Assets
Page 45
Databases
CloudKit Container
PrivatePrivatePrivate
PrivatePrivatePrivate
PrivatePrivatePrivate
Public Database
Page 46
Public Database
Databases
Private Database
CloudKit Container
Page 48
Databases
CKDatabase
Page 49
Databases
CKDatabase
Every app has access to two databases
Page 50
Databases
CKDatabase
Every app has access to two databases• Public Database
Page 51
Databases
CKDatabase
Every app has access to two databases• Public Database
• Private Database
Page 52
CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase]; !
CKDatabase *privateDatabase = [[CKContainer defaultContainer] privateCloudDatabase];
Databases
CKDatabase
Every app has access to two databases• Public Database
• Private Database
Page 53
Databases
Public Database Private Database
Page 54
Databases
Public Database Private Database
Data Type Shared Data Current User’s Data
Page 55
Databases
Public Database Private Database
Data Type Shared Data Current User’s Data
Account Required for Writing Required
Page 56
Databases
Public Database Private Database
Data Type Shared Data Current User’s Data
Account Required for Writing Required
Quota Developer User
Page 57
Databases
Public Database Private Database
Data Type Shared Data Current User’s Data
Account Required for Writing Required
Quota Developer User
Default Permissions World Readable User Readable
Page 58
Databases
Public Database Private Database
Data Type Shared Data Current User’s Data
Account Required for Writing Required
Quota Developer User
Default Permissions World Readable User Readable
Editing Permissions iCloud Dashboard Roles N/A
Page 59
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Assets
Page 60
Records
Public Database Private Database
CloudKit Container
Page 61
Public Database
Records
Record
Record
Record
Record
Record
Record
Page 64
CKRecord
Structured Data
Records
Page 65
CKRecord
Structured Data
Wraps key/value pairs
Records
Page 66
CKRecord
Structured Data
Wraps key/value pairs
Record Type
Records
Page 67
CKRecord
Structured Data
Wraps key/value pairs
Record Type
Just-in-time schema
Records
Page 68
CKRecord
Structured Data
Wraps key/value pairs
Record Type
Just-in-time schema
Metadata
Records
Page 69
Record Values
Records
Page 70
Record Values
Records
• NSString
• NSNumber
• NSData
• NSDate
Page 71
Record Values
• CLLocation
Records
• NSString
• NSNumber
• NSData
• NSDate
Page 72
Record Values
• CLLocation
Records
• CKReference
• CKAsset
• NSString
• NSNumber
• NSData
• NSDate
Page 73
Record Values
• CLLocation
Records
• CKReference
• CKAsset
• Arrays of the above
• NSString
• NSNumber
• NSData
• NSDate
Page 75
Records
@interface CKRecord : NSObject <NSSecureCoding, NSCopying>
Page 76
Records
@interface CKRecord : NSObject <NSSecureCoding, NSCopying>
- (instancetype)initWithRecordType:(NSString *)recordType;
Page 77
Records
@interface CKRecord : NSObject <NSSecureCoding, NSCopying>
- (instancetype)initWithRecordType:(NSString *)recordType;
- (id)objectForKey:(NSString *)key; - (void)setObject:(id <CKRecordValue>)object forKey:(NSString *)key;
Page 78
Records
@interface CKRecord : NSObject <NSSecureCoding, NSCopying>
- (instancetype)initWithRecordType:(NSString *)recordType;
- (id)objectForKey:(NSString *)key; - (void)setObject:(id <CKRecordValue>)object forKey:(NSString *)key;
- (id)objectForKeyedSubscript:(NSString *)key; - (void)setObject:(id <CKRecordValue>)object forKeyedSubscript:(NSString *)key;
Page 79
Records
@interface CKRecord : NSObject <NSSecureCoding, NSCopying>
- (instancetype)initWithRecordType:(NSString *)recordType;
- (id)objectForKey:(NSString *)key; - (void)setObject:(id <CKRecordValue>)object forKey:(NSString *)key;
- (id)objectForKeyedSubscript:(NSString *)key; - (void)setObject:(id <CKRecordValue>)object forKeyedSubscript:(NSString *)key;
- (NSArray /* NSString */ *)allKeys;
Page 81
CKRecord *party = [[CKRecord alloc] initWithRecordType:@“Party”];
Records
Page 82
// setting values [party setObject:@"Post Presentation Beers" forKey:@"summary"]; ! NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:30.0 * 60.0]; party[@"start"] = startDate;
CKRecord *party = [[CKRecord alloc] initWithRecordType:@“Party”];
Records
Page 83
// setting values [party setObject:@"Post Presentation Beers" forKey:@"summary"]; ! NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:30.0 * 60.0]; party[@"start"] = startDate;
CKRecord *party = [[CKRecord alloc] initWithRecordType:@“Party”];
Records
// accessing values NSString *summary = [party objectForKey:@"summary"]; ! NSDate *startDate = party[@"start"];
Page 84
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Assets
Page 85
Database
Record Zones
Record
Page 86
Database
Record
Record Zones
Record Zone
RecordRecordRecordRecordRecordRecord
Page 87
Database
Record Zone
Record Zones
RecordRecord
RecordRecord
RecordRecord
Record
Page 88
Database
Record Zones
RecordRecord
Record
Default Zone Custom Zone
RecordRecord
Record
RecordRecord
Record
RecordRecord
Record
Page 89
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Assets
Page 90
Record Identifiers
Page 91
Record Identifiers
@interface CKRecordID : NSObject <NSSecureCoding, NSCopying> ... @property (nonatomic, readonly, strong) NSString *recordName; @property (nonatomic, readonly, strong) CKRecordZoneID *zoneID; !@end
Page 92
Record Identifiers
@interface CKRecordID : NSObject <NSSecureCoding, NSCopying> ... @property (nonatomic, readonly, strong) NSString *recordName; @property (nonatomic, readonly, strong) CKRecordZoneID *zoneID; !@end
• Created by the client
Page 93
Record Identifiers
@interface CKRecordID : NSObject <NSSecureCoding, NSCopying> ... @property (nonatomic, readonly, strong) NSString *recordName; @property (nonatomic, readonly, strong) CKRecordZoneID *zoneID; !@end
• Created by the client
• Fully normalized: they represent the location of the record
Page 94
Record Identifiers
@interface CKRecordID : NSObject <NSSecureCoding, NSCopying> ... @property (nonatomic, readonly, strong) NSString *recordName; @property (nonatomic, readonly, strong) CKRecordZoneID *zoneID; !@end
• Created by the client
• Fully normalized: they represent the location of the record
• External data set foreign key
Page 95
Record Identifiers
Page 96
Record Identifiers
CKRecord *party = [[CKRecord alloc] initWithRecordType:@“Party”];
Page 97
Record Identifiers
CKRecord *party = [[CKRecord alloc] initWithRecordType:@“Party”];
@interface CKRecord : NSObject <NSSecureCoding, NSCopying> ... - (instancetype)initWithRecordType:(NSString *)recordType; - (instancetype)initWithRecordType:(NSString *)recordType recordID:(CKRecordID *)recordID; ... @end
Page 98
Record Identifiers
CKRecord *party = [[CKRecord alloc] initWithRecordType:@“Party”];
@interface CKRecord : NSObject <NSSecureCoding, NSCopying> ... - (instancetype)initWithRecordType:(NSString *)recordType; - (instancetype)initWithRecordType:(NSString *)recordType recordID:(CKRecordID *)recordID; ... @end
CKRecordID *wellKnownID = [[CKRecordID alloc] initWithRecordName:@"WellKnownParty"]; CKRecord *party = [[CKRecord alloc] initWithRecordType:@"Party" recordID:wellKnownID];
Page 99
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Assets
Page 100
Public Database
References
Party
Party
Clown
Clown
Clown
Page 101
Public Database
References
Party
Party
Clown
Clown
Clown
Page 103
CKReference
References
Page 104
CKReference
Server Understands Relationship
References
Page 105
CKReference
Server Understands Relationship
Cascade Deletes
References
Page 106
CKReference
Server Understands Relationship
Cascade Deletes
Dangling Pointers
References
Page 107
CKReference
Server Understands Relationship
Cascade Deletes
Dangling Pointers
Back References
References
Page 109
CKRecord *clown = [[CKRecord alloc] initWithRecordType:@“Clown”];
References
Page 110
CKRecord *clown = [[CKRecord alloc] initWithRecordType:@“Clown”];
References
CKRecord *party = [[CKRecord alloc] initWithRecordType:@“Party”]; CKReference *partyReference = [[CKReference alloc] initWithRecord:party action:CKReferenceActionNone]; clown[@"party"] = partyReference;
Page 111
CKRecord *clown = [[CKRecord alloc] initWithRecordType:@“Clown”];
References
CKRecord *party = [[CKRecord alloc] initWithRecordType:@“Party”]; CKReference *partyReference = [[CKReference alloc] initWithRecord:party action:CKReferenceActionNone]; clown[@"party"] = partyReference;
CKRecordID *wellKnownID = [[CKRecordID alloc] initWithRecordName:@"WellKnownParty"]; CKReference *wellKnownReference = [[CKReference alloc] initWithRecordID:wellKnownID action:CKReferenceActionNone]; clown[@"party"] = wellKnownReference;
Page 112
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Assets
Page 113
Assets
Public Database
Record
Record
Record
Record
Record
Record
Page 114
Container
Public Database
Assets
Record
RecordPost
Presentation
Get Together
Page 115
Assets
Container
Public Database Bulk Storage
Post
Presentation
Get Together
Record
Record
Page 116
Assets
Container
Public Database Bulk Storage
Record
Record
CKRecord CKAsset
PPGT
Page 117
Public Database
Assets
Container
Bulk Storage
Record
Record
PPGT
Page 120
CKAsset
Large, unstructured data
Assets
Page 121
CKAsset
Large, unstructured data
Files on disk
Assets
Page 122
CKAsset
Large, unstructured data
Files on disk
Owned by CKRecords
Assets
Page 123
CKAsset
Large, unstructured data
Files on disk
Owned by CKRecords
Garbage collected
Assets
Page 124
CKAsset
Large, unstructured data
Files on disk
Owned by CKRecords
Garbage collected
Efficient uploads and downloads
Assets
Page 126
NSURL *screenplayURL = [NSURL fileURLWithPath:@“...”]; ! CKAsset *screenplay = [[CKAsset alloc] initWithFileURL:screenplayURL];
Assets
Page 127
NSURL *screenplayURL = [NSURL fileURLWithPath:@“...”]; ! CKAsset *screenplay = [[CKAsset alloc] initWithFileURL:screenplayURL];
Assets
CKRecord *party = [[CKRecord alloc] initWithRecordType:@“Party”]; party[@"screenplay"] = screenplay;
Page 128
Fundamental CloudKit Objects
Containers
Databases
Records
Record Zones
Record Identifiers
References
Assets
Page 129
CloudKit's Convenience API
Page 131
Convenience API
Saving a record
Page 132
Convenience API
Saving a record
Fetching a record
Page 133
Convenience API
Saving a record
Fetching a record
Saving modified record
Page 134
Convenience API
Saving a record
Fetching a record
Saving modified record
Page 136
Saving a Record
CKRecordID *wellKnownID = [[CKRecordID alloc] initWithRecordName:@"WellKnownParty"]; CKRecord *party = [[CKRecord alloc] initWithRecordType:@"Party" recordID:wellKnownID];
Page 137
Saving a Record
CKRecordID *wellKnownID = [[CKRecordID alloc] initWithRecordName:@"WellKnownParty"]; CKRecord *party = [[CKRecord alloc] initWithRecordType:@"Party" recordID:wellKnownID];
CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];
Page 138
Saving a Record
CKRecordID *wellKnownID = [[CKRecordID alloc] initWithRecordName:@"WellKnownParty"]; CKRecord *party = [[CKRecord alloc] initWithRecordType:@"Party" recordID:wellKnownID];
CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];
[publicDatabase saveRecord:party completionHandler:^(CKRecord *savedParty, NSError *error) { ! // appropriate error handling when (error != nil) }];
Page 139
Convenience API
Saving a record
Fetching a record
Saving modified record
Page 140
Fetching a Record
Page 141
Fetching a Record
CKContainer *defaultContainer =[CKContainer defaultContainer]; CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
Page 142
Fetching a Record
CKContainer *defaultContainer =[CKContainer defaultContainer]; CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
CKRecordID *wellKnownID = [[CKRecordID alloc] initWithRecordName:@"WellKnownParty"];
Page 143
[publicDatabase fetchRecordWithID:wellKnownID completionHandler:^(CKRecord *fetchedParty, NSError *error) { ! // truly marvelous error handling when (error != nil) }];
Fetching a Record
CKContainer *defaultContainer =[CKContainer defaultContainer]; CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
CKRecordID *wellKnownID = [[CKRecordID alloc] initWithRecordName:@"WellKnownParty"];
Page 144
Convenience API
Saving a record
Fetching a record
Saving modified record
Page 145
Saving Modified Record
Page 146
Saving Modified Record
CKDatabase *publicDatabase = ...; CKRecordID *wellKnownID = ...; [publicDatabase fetchRecordWithID:wellKnownID completionHandler:^(CKRecord *fetchedParty, NSError *error) { if (error) { ... } else {
Page 147
Saving Modified Record
CKDatabase *publicDatabase = ...; CKRecordID *wellKnownID = ...; [publicDatabase fetchRecordWithID:wellKnownID completionHandler:^(CKRecord *fetchedParty, NSError *error) { if (error) { ... } else {
NSDate *endDate = fetchedParty[@"end"]; fetchedParty[@"end"] = [endDate dateByAddingTimeInterval:30.0 * 60.0];
Page 148
Saving Modified Record
CKDatabase *publicDatabase = ...; CKRecordID *wellKnownID = ...; [publicDatabase fetchRecordWithID:wellKnownID completionHandler:^(CKRecord *fetchedParty, NSError *error) { if (error) { ... } else {
NSDate *endDate = fetchedParty[@"end"]; fetchedParty[@"end"] = [endDate dateByAddingTimeInterval:30.0 * 60.0];
[publicDatabase saveRecord:fetchedParty completionHandler:^(CKRecord *savedParty, NSError *saveError) { ! // error handling to make your mother proud when (error != nil) }]; } }];
Page 149
Convenience API
Saving a record
Fetching a record
Saving modified record
Page 150
Big Data, Tiny Phone
Page 151
Big Data, Tiny Phone
Page 152
Big Data, Tiny Phone
Keep your large data in the cloud
Page 153
Big Data, Tiny Phone
Keep your large data in the cloud
Client views slice of that data
Page 154
Big Data, Tiny Phone
Keep your large data in the cloud
Client views slice of that data
Client view can change
Page 155
Big Data, Tiny Phone
Keep your large data in the cloud
Client views slice of that data
Client view can change
Clients use queries to focus their viewpoint
Page 158
Queries
CKQuery
Combine a RecordType, a NSPredicate, and NSSortDescriptors
Page 159
Queries
CKQuery
Combine a RecordType, a NSPredicate, and NSSortDescriptors• CloudKit supports a subset of NSPredicate
Page 160
QueriesPredicates
Page 161
QueriesPredicates
[NSPredicate predicateWithFormat:@"name = %@", partyName];
Page 162
QueriesPredicates
[NSPredicate predicateWithFormat:@"name = %@", partyName];
[NSPredicate predicateWithFormat:@"%K = %@", dynamicKey, value];
Page 163
QueriesPredicates
[NSPredicate predicateWithFormat:@"name = %@", partyName];
[NSPredicate predicateWithFormat:@"%K = %@", dynamicKey, value];
[NSPredicate predicateWithFormat:@"start > %@", [NSDate date]];
Page 164
QueriesPredicates
[NSPredicate predicateWithFormat:@"name = %@", partyName];
[NSPredicate predicateWithFormat:@"%K = %@", dynamicKey, value];
[NSPredicate predicateWithFormat:@"start > %@", [NSDate date]];
CLLocation *location = [[CLLocation alloc] initWithLatitude:37.783 longitude:-122.404]; [NSPredicate predicateWithFormat:@"distanceToLocation:fromLocation:(Location, %@) < 100", location];
Page 165
QueriesPredicates
[NSPredicate predicateWithFormat:@"name = %@", partyName];
[NSPredicate predicateWithFormat:@"%K = %@", dynamicKey, value];
[NSPredicate predicateWithFormat:@"start > %@", [NSDate date]];
CLLocation *location = [[CLLocation alloc] initWithLatitude:37.783 longitude:-122.404]; [NSPredicate predicateWithFormat:@"distanceToLocation:fromLocation:(Location, %@) < 100", location];
[NSPredicate predicateWithFormat:@"ALL tokenize(%@, 'Cdl') IN allTokens", @"after session"];
Page 166
QueriesPredicates
[NSPredicate predicateWithFormat:@"name = %@", partyName];
[NSPredicate predicateWithFormat:@"%K = %@", dynamicKey, value];
[NSPredicate predicateWithFormat:@"start > %@", [NSDate date]];
CLLocation *location = [[CLLocation alloc] initWithLatitude:37.783 longitude:-122.404]; [NSPredicate predicateWithFormat:@"distanceToLocation:fromLocation:(Location, %@) < 100", location];
[NSPredicate predicateWithFormat:@"ALL tokenize(%@, 'Cdl') IN allTokens", @"after session"];
[NSPredicate predicateWithFormat:@"name = %@ AND startDate > %@", partyName, [NSDate date]];
Page 167
QueriesCreating
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"start > %@", [NSDate date]]; ! CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Party" predicate:predicate];
Page 168
QueriesPerforming
Page 169
QueriesPerforming
CKQuery *query = ...; ! [[publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {
Page 170
QueriesPerforming
CKQuery *query = ...; ! [[publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {
// astounding error handling when (error != nil) ! if (!error) { NSLog(@"Fetch %ld results", (long)[results count]); for (CKRecord *record in results) { NSLog(@"Found matching party %@", record); } } }];
Page 171
Big Data, Tiny Phone
Page 172
Big Data, Tiny Phone
Queries are polls
Page 173
Big Data, Tiny Phone
Queries are polls
Great for slicing through large server data
Page 174
Big Data, Tiny Phone
Queries are polls
Great for slicing through large server data
Bad for large, mostly static data set
Page 175
Big Data, Tiny Phone
Queries are polls
Great for slicing through large server data
Bad for large, mostly static data set• Battery life
Page 176
Big Data, Tiny Phone
Queries are polls
Great for slicing through large server data
Bad for large, mostly static data set• Battery life
• Networking traffic
Page 177
Big Data, Tiny Phone
Queries are polls
Great for slicing through large server data
Bad for large, mostly static data set• Battery life
• Networking traffic
• User experience
Page 178
Big Data, Tiny Phone
Queries are polls
Great for slicing through large server data
Bad for large, mostly static data set• Battery life
• Networking traffic
• User experience
What you want is the server running your query
Page 179
Big Data, Tiny Phone
Queries are polls
Great for slicing through large server data
Bad for large, mostly static data set• Battery life
• Networking traffic
• User experience
What you want is the server running your query• ... in the background
Page 180
Big Data, Tiny Phone
Queries are polls
Great for slicing through large server data
Bad for large, mostly static data set• Battery life
• Networking traffic
• User experience
What you want is the server running your query• ... in the background
• ... after every record save
Page 181
Big Data, Tiny Phone
Queries are polls
Great for slicing through large server data
Bad for large, mostly static data set• Battery life
• Networking traffic
• User experience
What you want is the server running your query• ... in the background
• ... after every record save
• ... and you want push
Page 183
Subscriptions
CKSubscription
Page 184
Subscriptions
CKSubscription
Combine a RecordType, a NSPredicate, and Push
Page 185
Subscriptions
CKSubscription
Combine a RecordType, a NSPredicate, and Push• Push via Apple Push Service
Page 186
Subscriptions
CKSubscription
Combine a RecordType, a NSPredicate, and Push• Push via Apple Push Service
• Augmented payload
Page 187
New parties In the future
Alert with "Party Time!"
SubscriptionsSubscriptionsNew parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
Page 188
Subscriptions
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
Page 189
Subscriptions
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
Page 190
Party Tonight
E31970FB
Subscriptions
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
Page 191
Subscriptions
Party Tonight
E31970FB
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
Page 192
Subscriptions
Party Tonight
E31970FB
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
Page 193
Subscriptions
Party Tonight
E31970FB
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
Page 194
Subscriptions
Party Tonight
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
E31970FB
Page 195
Subscriptions
Party Tonight
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
E31970FB
Page 196
Subscriptions
Party Time!
Party Tonight
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
E31970FB
Page 197
Subscriptions
Party Tonight
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
Party Time!E31970FB
Page 198
Subscriptions
Party Tonight
New parties In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
New parties
In the future
Alert with "Party Time!"
Party Time!E31970FB
Page 199
SubscriptionsCreating
Page 200
SubscriptionsCreating
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"start > %@", [NSDate date]];
Page 201
SubscriptionsCreating
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"start > %@", [NSDate date]];
CKSubscription *subscription = [[CKSubscription alloc] initWithRecordType:@"Party" predicate:predicate options:CKSubscriptionOptionsFiresOnRecordCreation];
Page 202
SubscriptionsCreating
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"start > %@", [NSDate date]];
CKSubscription *subscription = [[CKSubscription alloc] initWithRecordType:@"Party" predicate:predicate options:CKSubscriptionOptionsFiresOnRecordCreation];
CKNotificationInfo *notificationInfo = [CKNotificationInfo new]; notificationInfo.alertLocalizationKey = @"LOCAL_NOTIFICATION_KEY"; notificationInfo.soundName = @"Party.aiff"; notificationInfo.shouldBadge = YES;
Page 203
SubscriptionsCreating
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"start > %@", [NSDate date]];
CKSubscription *subscription = [[CKSubscription alloc] initWithRecordType:@"Party" predicate:predicate options:CKSubscriptionOptionsFiresOnRecordCreation];
CKNotificationInfo *notificationInfo = [CKNotificationInfo new]; notificationInfo.alertLocalizationKey = @"LOCAL_NOTIFICATION_KEY"; notificationInfo.soundName = @"Party.aiff"; notificationInfo.shouldBadge = YES;
subscription.notificationInfo = notificationInfo;
Page 204
SubscriptionsSaving
CKSubscription *subscription = ...; ! [[publicDatabase saveSubscription:subscription completionHandler:^(CKSubscription *subscription, NSError *error) { ! // labor-of-love error handling when (error != nil) }];
Page 205
SubscriptionsHandling push
Page 206
SubscriptionsHandling push
@implementation AppDelegate ! - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
Page 207
SubscriptionsHandling push
@implementation AppDelegate ! - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
CKNotification *cloudKitNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
Page 208
SubscriptionsHandling push
@implementation AppDelegate ! - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
CKNotification *cloudKitNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
NSString *alertBody = cloudKitNotification.alertBody;
Page 209
SubscriptionsHandling push
@implementation AppDelegate ! - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
CKNotification *cloudKitNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
NSString *alertBody = cloudKitNotification.alertBody;
if (cloudKitNotification.notificationType == CKNotificationTypeQuery) { CKQueryNotification *queryNotification = cloudKitNotification; CKRecordID *recordID = [queryNotification recordID]; } }
Page 210
CloudKit User Accounts
Page 211
CloudKit User Accounts
Page 212
CloudKit User Accounts
Identity
Page 213
CloudKit User Accounts
Identity
Metadata
Page 214
CloudKit User Accounts
Identity
Metadata
Privacy
Page 215
CloudKit User Accounts
Identity
Metadata
Privacy
Discovery
Page 216
CloudKit User Accounts
Identity
Metadata
Privacy
Discovery
Page 217
User Identity
Container
Private Private
Private
Private
Private PrivatePrivate
PrivatePrivate
Page 218
User Identity
Container
Private Private
Private
Private
Private PrivatePrivate
PrivatePrivate
Page 219
User Identity
Container
Private Private
Private
Private
Private PrivatePrivate
PrivatePrivate
Page 227
User Identity
User Record ID
Page 228
User Identity
User Record ID
Stable identifier for this user
Page 229
User Identity
User Record ID
Stable identifier for this user
Scoped to the container
Page 230
User Identity
User Record ID
Stable identifier for this user
Scoped to the container
Independent API
Page 231
User Identity
[[CKContainer defaultContainer] fetchUserRecordIDWithCompletionHandler: ^(CKRecordID *userRecordID, NSError *error) { ! // ostentatious error handling when (error != nil) }];
Page 232
CloudKit User Accounts
Identity
Metadata
Privacy
Discovery
Page 234
User Metadata
Container
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 235
User Metadata
Container
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 236
User Metadata
User RecordContainer
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 237
User Metadata
User Record
One per database Container
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 238
User Metadata
User Record
One per database
World readable in public database
Container
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 239
User Metadata
User Record
One per database
World readable in public database
Treated like ordinary record
Container
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 240
User Metadata
User Record
One per database
World readable in public database
Treated like ordinary record• CKRecordTypeUserRecord
Container
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 241
User Metadata
User Record
One per database
World readable in public database
Treated like ordinary record• CKRecordTypeUserRecord
… mostly
Container
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 242
User Metadata
User Record
One per database
World readable in public database
Treated like ordinary record• CKRecordTypeUserRecord
… mostly• Reserved by system
Container
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 243
User Metadata
User Record
One per database
World readable in public database
Treated like ordinary record• CKRecordTypeUserRecord
… mostly• Reserved by system
• Cannot be queried
Container
Public Database
User 9ECDC8B9
User 54C16AB7
User 4B20C8B2
User 843AE1DC
Private Database
User 9ECDC8B9
9ECDC8B9
Page 244
User Metadata
CKContainer *defaultContainer =[CKContainer defaultContainer]; CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase]; ! [defaultContainer fetchUserRecordIDWithCompletionHandler: ^(CKRecordID *userRecordID, NSError *error) { if (error) { ... } else {
Page 245
User Metadata
CKContainer *defaultContainer =[CKContainer defaultContainer]; CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase]; ! [defaultContainer fetchUserRecordIDWithCompletionHandler: ^(CKRecordID *userRecordID, NSError *error) { if (error) { ... } else {
[publicDatabase fetchRecordWithID:userRecordID completionHandler:^(CKRecord *userRecord, NSError *error) {
Page 246
User Metadata
CKContainer *defaultContainer =[CKContainer defaultContainer]; CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase]; ! [defaultContainer fetchUserRecordIDWithCompletionHandler: ^(CKRecordID *userRecordID, NSError *error) { if (error) { ... } else {
[publicDatabase fetchRecordWithID:userRecordID completionHandler:^(CKRecord *userRecord, NSError *error) {
if (error) { ... } else { NSString *partyName = userRecord[@"partyName"]; NSLog(@"Fetched record for %@:%@", partyName, userRecord); } }]; } }];
Page 247
CloudKit User Accounts
Identity
Metadata
Privacy
Discovery
Page 249
User Privacy
No disclosure by default
Page 250
User Privacy
No disclosure by default
Disclosure requested by application
Page 251
User Privacy
No disclosure by default
Disclosure requested by application
Page 252
CloudKit User Accounts
Identity
Metadata
Privacy
Discovery
Page 278
User Discovery
Input • User RecordID
• Email address
• Entire address book
Page 279
User Discovery
Input • User RecordID
• Email address
• Entire address book
Output
Page 280
User Discovery
Input • User RecordID
• Email address
• Entire address book
Output• User RecordID
Page 281
User Discovery
Input • User RecordID
• Email address
• Entire address book
Output• User RecordID
• First and last names
Page 282
User Discovery
Input • User RecordID
• Email address
• Entire address book
Output• User RecordID
• First and last names
Personally identifying information
Page 283
User Discovery
Input • User RecordID
• Email address
• Entire address book
Output• User RecordID
• First and last names
Personally identifying information• Requires opt-in
Page 284
User Discovery
CKContainer *defaultContainer = [CKContainer defaultContainer];
Page 285
User Discovery
CKContainer *defaultContainer = [CKContainer defaultContainer];
[defaultContainer discoverAllContactUserInfosWithCompletionHandler: ^(NSArray *userInfos, NSError *error) {
Page 286
User Discovery
CKContainer *defaultContainer = [CKContainer defaultContainer];
[defaultContainer discoverAllContactUserInfosWithCompletionHandler: ^(NSArray *userInfos, NSError *error) {
if (error) { ... } else { for (CKDiscoveredUserInfo *userInfo in userInfos) { NSLog(@"%@: %@ %@", userInfo.userRecordID, userInfo.firstName, userInfo.lastName); } } }];
Page 287
CloudKit User Accounts
Identity
Metadata
Privacy
Discovery
Page 288
When to Use CloudKit
Page 289
When to Use CloudKit
iCloud Key Value Store
iCloud Drive
iCloud Core Data
CloudKit
Page 290
When to Use CloudKit
iCloud Key Value Store
iCloud Drive
iCloud Core Data
CloudKit
• Asynchronously kept up to date
• Data limit constraints
• Great for application preferences
Page 291
When to Use CloudKit
iCloud Key Value Store
iCloud Drive
iCloud Core Data
CloudKit
• Simple API
• Full offline cache on OS X
• Unstructured
• Tied to the filesystem
• Great for document centric apps
Page 292
When to Use CloudKit
iCloud Key Value Store
iCloud Drive
iCloud Core Data
CloudKit
• Data replicated to all devices
• Data is single-user
• Great for keeping private, structured data in sync
Page 293
When to Use CloudKit
iCloud Key Value Store
iCloud Drive
iCloud Core Data
• Public data
• Structured and bulk data
• Large data set
• Use iCloud accounts
• Client directed data transfer
CloudKit
Page 295
Summary
Access to iCloud servers
Page 296
Summary
Access to iCloud servers
Public and private data
Page 297
Summary
Access to iCloud servers
Public and private data
Structured and bulk data
Page 298
Summary
Access to iCloud servers
Public and private data
Structured and bulk data
Leverage iCloud accounts
Page 299
Summary
Access to iCloud servers
Public and private data
Structured and bulk data
Leverage iCloud accounts
Apple is building on it
Page 300
Summary
Access to iCloud servers
Public and private data
Structured and bulk data
Leverage iCloud accounts
Apple is building on it
We're excited to see what you're going to build on this
Page 301
More Information
Dave DeLong App Frameworks Evangelist [email protected]
CloudKit Framework Reference http://developer.apple.com
Apple Developer Forums http://devforums.apple.com
Page 302
Related Sessions
• Advanced CloudKit Mission Thursday 3:15PM
Page 303
Labs
• CloudKit Lab Services Lab A Tuesday 4:30PM
• CloudKit Lab Frameworks Lab B Wednesday 12:45PM
• CloudKit Lab Frameworks Lab A Friday 11:30AM