Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CloudKit - Just another iCloud API?

Michael Ochs
December 15, 2014

CloudKit - Just another iCloud API?

This is a talk about the structure and my first steps with CloudKit. It shows how you could start learning CloudKit and lists a number of pitfalls I ran into, when I started my first project.

Michael Ochs

December 15, 2014
Tweet

More Decks by Michael Ochs

Other Decks in Programming

Transcript

  1. CloudKit
    Just another iCloud API?
    Michael Ochs

    View full-size slide

  2. What is CloudKit?

    View full-size slide

  3. What is CloudKit?
    “The CloudKit framework provides interfaces for moving data between
    your app and your iCloud containers”
    - CloudKit Framework Reference

    View full-size slide

  4. What is CloudKit?
    “CloudKit is not a replacement for your app’s existing data objects.
    Instead, CloudKit provides complementary services for managing the
    transfer of data to and from iCloud servers.”
    - CloudKit Framework Reference

    View full-size slide

  5. What is CloudKit?
    • it is a transfer api to move data to and from the cloud
    • it behaves like a remote database in many parts
    • but: It is not your app’s database

    View full-size slide

  6. Structure
    • local api
    • remote database
    • remote dashboard
    • records
    • references
    • containers
    • queries
    • operations
    • zones
    • subscriptions

    View full-size slide

  7. Structure
    Data
    Container
    Public Database
    Private
    Database
    Private
    Database
    Private
    Database
    Private
    Database
    Private
    Database
    Private
    Database
    Private
    Database
    Private
    Database
    Private
    Database
    Bulk Storage
    Zone
    Record
    Record
    Record
    Asset
    Asset
    Asset

    View full-size slide

  8. Structure
    Convenience API
    CKContainer CKDatabase
    CKRecord

    View full-size slide

  9. Structure
    Convenience API
    CKContainer CKDatabase
    CKRecord
    CKQuery

    View full-size slide

  10. Structure
    Convenience API
    CKContainer CKDatabase CKSubscription

    View full-size slide

  11. Structure
    Not so convenient API
    CKContainer CKDatabase
    CKRecord
    CKModifyRecordsOperation
    CKRecord
    CKQueryOperation
    CKQuery
    CKRecord
    CKModifySubscriptionsOperation
    CKSubscription

    View full-size slide

  12. Structure
    Inconvenient API
    CKModifyRecordsOperation
    CKQueryOperation
    CKModifySubscriptionsOperation CKModifyRecordZonesOperation
    CKFetchSubscriptionsOperation CKFetchRecordZonesOperation
    CKFetchRecordsOperation
    CKFetchRecordChangesOperation
    CKDiscoverAllContactsOperation
    CKDiscoverUserInfosOperation
    CKFetchNotificationChangesOperation
    CKMarkNotificationsReadOperation
    CKModifyBadgeOperation

    View full-size slide

  13. CKModifyRecordsOperation
    CKQueryOperation
    CKModifySubscriptionsOperation CKModifyRecordZonesOperation
    CKFetchSubscriptionsOperation CKFetchRecordZonesOperation
    CKFetchRecordsOperation
    CKFetchRecordChangesOperation
    CKDiscoverAllContactsOperation
    CKDiscoverUserInfosOperation
    CKFetchNotificationChangesOperation
    CKMarkNotificationsReadOperation
    CKModifyBadgeOperation
    Structure
    InternalError
    PartialFailure
    NetworkUnavailable
    NetworkFailure
    BadContainer
    ServiceUnavailable
    RequestRateLimited
    MissingEntitlement
    NotAuthenticated
    PermissionFailure
    UnknownItem
    InvalidArguments
    ResultsTruncated
    ServerRecordChanged
    ServerRejectedRequest
    AssetFileNotFound
    AssetFileModified
    IncompatibleVersion
    ConstraintViolation
    OperationCancelled
    ChangeTokenExpired
    BatchRequestFailed
    ZoneBusy
    BadDatabase
    QuotaExceeded
    ZoneNotFound

    View full-size slide

  14. CKModifyRecordsOperation
    CKQueryOperation
    CKModifySubscriptionsOperation CKModifyRecordZonesOperation
    CKFetchSubscriptionsOperation CKFetchRecordZonesOperation
    CKFetchRecordsOperation
    CKFetchRecordChangesOperation
    CKDiscoverAllContactsOperation
    CKDiscoverUserInfosOperation
    CKFetchNotificationChangesOperation
    CKMarkNotificationsReadOperation
    CKModifyBadgeOperation
    Structure
    Do not start with CloudKit in your productive application!

    View full-size slide

  15. Structure
    • This api has nothing to do with convenience
    • …but this api is great
    • It gives you a lot of responsibility
    • …but also a lot of power and flexibility

    View full-size slide

  16. Dashboard
    • Web based administration
    • View, create, edit, and remove records
    • Edit, and remove record layouts
    • Edit access groups / privileges

    View full-size slide

  17. CKDatabase
    • Public database
    • readable by everyone
    • writable by every iCloud user
    • Private database
    • readable and writable by the current iCloud user

    View full-size slide

  18. CKDatabase
    CKContainer *container = [CKContainer defaultContainer];

    CKDatabase *database = [container publicCloudDatabase];
    Public Database

    View full-size slide

  19. CKDatabase
    CKContainer *container = [CKContainer defaultContainer];

    CKDatabase *database = [container publicCloudDatabase];
    Public Database

    View full-size slide

  20. CKDatabase
    CKContainer *container = [CKContainer defaultContainer];

    CKDatabase *database = [container publicCloudDatabase];
    Public Database

    View full-size slide

  21. CKDatabase
    CKContainer *container = [CKContainer defaultContainer];

    CKDatabase *database = [container privateCloudDatabase];
    Private Database

    View full-size slide

  22. CKDatabase
    CKContainer *container = [CKContainer defaultContainer];

    CKDatabase *database = [container privateCloudDatabase];
    Private Database

    View full-size slide

  23. CKDatabase
    CKRecordID *recordID = …;

    [database deleteRecordWithID:recordID

    completionHandler:^(CKRecordID *recordID, NSError *error) {

    if (error) {

    dispatch_async(dispatch_get_main_queue(), ^{

    [self presentError:error

    completionHandler:^(BOOL didRecover){

    // TODO: handle error

    }];

    });

    return;

    }


    // TODO: handle success

    }];

    View full-size slide

  24. CKDatabase
    CKRecordID *recordID = …;

    [database deleteRecordWithID:recordID

    completionHandler:^(CKRecordID *recordID, NSError *error) {

    if (error) {

    dispatch_async(dispatch_get_main_queue(), ^{

    [self presentError:error

    completionHandler:^(BOOL didRecover){

    // TODO: handle error

    }];

    });

    return;

    }


    // TODO: handle success

    }];

    View full-size slide

  25. CKDatabase
    CKRecordID *recordID = …;

    [database deleteRecordWithID:recordID

    completionHandler:^(CKRecordID *recordID, NSError *error) {

    if (error) {

    dispatch_async(dispatch_get_main_queue(), ^{

    [self presentError:error

    completionHandler:^(BOOL didRecover){

    // TODO: handle error

    }];

    });

    return;

    }


    // TODO: handle success

    }];

    View full-size slide

  26. CKRecord
    • Data object
    • Dictionary like api
    • On the fly model generation

    View full-size slide

  27. CKRecord
    Each record has a…
    • …record type
    • …record id
    • …creation date / user record id
    • …modification date / user record id

    View full-size slide

  28. CKRecord
    Class
    record type
    record id
    creation date / user record id
    modification date / user record id
    Type
    NSString*
    CKRecordID*
    NSDate* / CKRecordID*
    NSDate* / CKRecordID*

    View full-size slide

  29. CKRecord
    CloudKit
    record type
    record id
    creation date / user record id
    modification date / user record id
    CoreData
    entity name
    object id
    n/a
    n/a

    View full-size slide

  30. CKRecord
    CKRecord *record = [[CKRecord alloc] initWithRecordType:@"Todo"];

    record[@"title"] = @"Get christmas presents";


    [database saveRecord:record

    completionHandler:^(CKRecord *record, NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store record id to your local model

    }];

    View full-size slide

  31. CKRecord
    CKRecord *record = [[CKRecord alloc] initWithRecordType:@"Todo"];

    record[@"title"] = @"Get christmas presents";


    [database saveRecord:record

    completionHandler:^(CKRecord *record, NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store record id to your local model

    }];

    View full-size slide

  32. CKRecord
    CKRecord *record = [[CKRecord alloc] initWithRecordType:@"Todo"];

    record[@"title"] = @"Get christmas presents";


    [database saveRecord:record

    completionHandler:^(CKRecord *record, NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store record id to your local model

    }];

    View full-size slide

  33. CKRecord
    CKRecord *record = [[CKRecord alloc] initWithRecordType:@"Todo"];

    record[@"title"] = @"Get christmas presents";


    [database saveRecord:record

    completionHandler:^(CKRecord *record, NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store record id to your local model

    }];

    View full-size slide

  34. CKRecord
    CKRecord *record = [[CKRecord alloc] initWithRecordType:@"Todo"];

    record[@"title"] = @"Get christmas presents";


    [database saveRecord:record

    completionHandler:^(CKRecord *record, NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store record id to your local model

    }];

    View full-size slide

  35. Container
    Public Database
    Zone
    CKRecord
    Todo
    creationDate
    modificationDate
    title

    View full-size slide

  36. CKRecord
    CKRecordID *recordID = …; // get record id from your model

    [database fetchRecordWithID:recordID

    completionHandler:^(CKRecord *record, NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    record[@"done"] = @YES;


    [database saveRecord:record

    completionHandler:^(CKRecord *record, NSError *error) {

    // TODO: check & handle error

    }];

    }];

    View full-size slide

  37. CKRecord
    CKRecordID *recordID = …; // get record id from your model

    [database fetchRecordWithID:recordID

    completionHandler:^(CKRecord *record, NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    record[@"done"] = @YES;


    [database saveRecord:record

    completionHandler:^(CKRecord *record, NSError *error) {

    // TODO: check & handle error

    }];

    }];

    View full-size slide

  38. CKRecord
    CKRecordID *recordID = …; // get record id from your model

    [database fetchRecordWithID:recordID

    completionHandler:^(CKRecord *record, NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    record[@"done"] = @YES;


    [database saveRecord:record

    completionHandler:^(CKRecord *record, NSError *error) {

    // TODO: check & handle error

    }];

    }];

    View full-size slide

  39. CKRecord
    CKRecordID *recordID = …; // get record id from your model

    [database fetchRecordWithID:recordID

    completionHandler:^(CKRecord *record, NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    record[@"done"] = @YES;


    [database saveRecord:record

    completionHandler:^(CKRecord *record, NSError *error) {

    // TODO: check & handle error

    }];

    }];

    View full-size slide

  40. Container
    Public Database
    Zone
    CKRecord
    Todo
    creationDate
    modificationDate
    title
    Todo
    creationDate
    modificationDate
    title
    done

    View full-size slide

  41. CKSubscription

    View full-size slide

  42. CKSubscription
    • Subscribe to push notifications
    • Bound to a record type & predicate
    • on create / on update / on delete
    • silent / badge / alert / sound

    View full-size slide

  43. CKSubscription
    • Configure push notifications
    • Register for push notifications
    • Subscribe to cloud kit

    View full-size slide

  44. CKSubscription
    - (BOOL)application:(UIApplication *)application

    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [application registerForRemoteNotifications];

    return YES;

    }


    - (void)application:(UIApplication *)application

    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

    // trigger subscription

    }

    View full-size slide

  45. CKSubscription
    - (BOOL)application:(UIApplication *)application

    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [application registerForRemoteNotifications];

    return YES;

    }


    - (void)application:(UIApplication *)application

    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

    // trigger subscription

    }

    View full-size slide

  46. CKSubscription
    - (BOOL)application:(UIApplication *)application

    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [application registerForRemoteNotifications];

    return YES;

    }


    - (void)application:(UIApplication *)application

    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

    // trigger subscription

    }

    View full-size slide

  47. CKSubscription
    NSPredicate *predicate = [NSPredicate predicateWithValue:YES];


    CKSubscriptionOptions options = (

    CKSubscriptionOptionsFiresOnRecordUpdate |

    CKSubscriptionOptionsFiresOnRecordDeletion |

    CKSubscriptionOptionsFiresOnRecordCreation);


    CKSubscription *subscription = [[CKSubscription alloc]

    initWithRecordType:@"Todo"

    predicate:predicate

    options:options];

    View full-size slide

  48. CKSubscription
    NSPredicate *predicate = [NSPredicate predicateWithValue:YES];


    CKSubscriptionOptions options = (

    CKSubscriptionOptionsFiresOnRecordUpdate |

    CKSubscriptionOptionsFiresOnRecordDeletion |

    CKSubscriptionOptionsFiresOnRecordCreation);


    CKSubscription *subscription = [[CKSubscription alloc]

    initWithRecordType:@"Todo"

    predicate:predicate

    options:options];

    View full-size slide

  49. CKSubscription
    NSPredicate *predicate = [NSPredicate predicateWithValue:YES];


    CKSubscriptionOptions options = (

    CKSubscriptionOptionsFiresOnRecordUpdate |

    CKSubscriptionOptionsFiresOnRecordDeletion |

    CKSubscriptionOptionsFiresOnRecordCreation);


    CKSubscription *subscription = [[CKSubscription alloc]

    initWithRecordType:@"Todo"

    predicate:predicate

    options:options];

    View full-size slide

  50. CKSubscription
    NSPredicate *predicate = [NSPredicate predicateWithValue:YES];


    CKSubscriptionOptions options = (

    CKSubscriptionOptionsFiresOnRecordUpdate |

    CKSubscriptionOptionsFiresOnRecordDeletion |

    CKSubscriptionOptionsFiresOnRecordCreation);


    CKSubscription *subscription = [[CKSubscription alloc]

    initWithRecordType:@"Todo"

    predicate:predicate

    options:options];

    View full-size slide

  51. CKSubscription
    CKNotificationInfo *notificationInfo = [CKNotificationInfo new];

    notificationInfo.shouldSendContentAvailable = YES;


    subscription.notificationInfo = notificationInfo;


    [database saveSubscription:subscription

    completionHandler:^(CKSubscription *subscription,

    NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store subscription id

    }];

    View full-size slide

  52. CKSubscription
    CKNotificationInfo *notificationInfo = [CKNotificationInfo new];

    notificationInfo.shouldSendContentAvailable = YES;


    subscription.notificationInfo = notificationInfo;


    [database saveSubscription:subscription

    completionHandler:^(CKSubscription *subscription,

    NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store subscription id

    }];

    View full-size slide

  53. CKSubscription
    CKNotificationInfo *notificationInfo = [CKNotificationInfo new];

    notificationInfo.shouldSendContentAvailable = YES;


    subscription.notificationInfo = notificationInfo;


    [database saveSubscription:subscription

    completionHandler:^(CKSubscription *subscription,

    NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store subscription id

    }];

    View full-size slide

  54. CKSubscription
    CKNotificationInfo *notificationInfo = [CKNotificationInfo new];

    notificationInfo.shouldSendContentAvailable = YES;


    subscription.notificationInfo = notificationInfo;


    [database saveSubscription:subscription

    completionHandler:^(CKSubscription *subscription,

    NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store subscription id

    }];

    View full-size slide

  55. CKSubscription
    CKNotificationInfo *notificationInfo = [CKNotificationInfo new];

    notificationInfo.shouldSendContentAvailable = YES;


    subscription.notificationInfo = notificationInfo;


    [database saveSubscription:subscription

    completionHandler:^(CKSubscription *subscription,

    NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store subscription id

    }];

    View full-size slide

  56. CKSubscription
    CKNotificationInfo *notificationInfo = [CKNotificationInfo new];

    notificationInfo.shouldSendContentAvailable = YES;


    subscription.notificationInfo = notificationInfo;


    [database saveSubscription:subscription

    completionHandler:^(CKSubscription *subscription,

    NSError *error) {

    if (error) {

    // TODO: handle error

    return;

    }

    // TODO: store subscription id

    }];

    View full-size slide

  57. CKSubscription
    - (void)application:(UIApplication *)application

    didReceiveRemoteNotification:(NSDictionary *)userInfo

    fetchCompletionHandler:

    (void(^)(UIBackgroundFetchResult))completionHandler {


    // TODO: fetch updates and handle them

    completionHandler(UIBackgroundFetchResultNewData);


    }

    View full-size slide

  58. CKSubscription
    - (void)application:(UIApplication *)application

    didReceiveRemoteNotification:(NSDictionary *)userInfo

    fetchCompletionHandler:

    (void(^)(UIBackgroundFetchResult))completionHandler {


    // TODO: fetch updates and handle them

    completionHandler(UIBackgroundFetchResultNewData);


    }

    View full-size slide

  59. CKSubscription
    - (void)application:(UIApplication *)application

    didReceiveRemoteNotification:(NSDictionary *)userInfo

    fetchCompletionHandler:

    (void(^)(UIBackgroundFetchResult))completionHandler {


    // TODO: fetch updates and handle them

    completionHandler(UIBackgroundFetchResultNewData);


    }

    View full-size slide

  60. CKSubscription
    • mark notifications as read
    • fetch missed notifications
    • handle badges on all devices

    View full-size slide

  61. // TODO: handle error

    View full-size slide

  62. // TODO: handle error
    • You need to handle them
    • Otherwise your data models will become inconsistent
    • They might occur often

    View full-size slide

  63. // TODO: handle error
    • Handle errors in a central place if possible
    • HRSCustomErrorHandling might help you

    View full-size slide

  64. // TODO: handle error
    CloudKit
    Transaction cache
    Data model

    View full-size slide

  65. // TODO: handle error
    CloudKit
    Transaction cache
    Data model

    View full-size slide

  66. Problems
    • convenient API can not handle complexity of CloudKit
    • lack of documentation
    • strange behavior
    • iOS simulator is not working
    • privileges handling is lacking features

    View full-size slide

  67. Next steps
    • experiment with the convenient api
    • check if CloudKit is the right iCloud api for your task
    • move to the operation based api
    • get your models together

    View full-size slide

  68. Feedback / Questions
    @_mochs
    ios-coding.com

    View full-size slide