8

I'm building an app that heavily relies on CloudKit for data synchronization. Each time the app launches, it catches up with all changes that have been made on the server using a CKFetchNotificationChangesOperation. This successfully returns all objects that have been created and/or modified, but I am now at the stage that I also want my app to delete records based on these messages.

In my app, each object that I have stored in CoreData also carries the recordID of the online representation of that object. This way I can easily pick up objects I need to modify.

This seems hard when deleting objects, as CloudKit only returns recordID's for these objects, and does not provide a recordType that I can use to know what object I am looking for in my CoreData Database.

Question

How does one correctly handle CloudKit 'deleted' notifications in a case with multiple record types?

4
  • 1
    Do you generate the recordID or is it something CloudKit generates for you? And does CloudKit really not give you any information about the CK record type that was deleted? Commented Jul 31, 2017 at 17:05
  • 1
    I'm not sure I follow: you have notifications on multiple record types, and you want to delete the records when you receive notifs? Or, you're receiving notifications on deleted records and you're trying to understand what was deleted? Commented Jul 31, 2017 at 18:23
  • Thanks for the reply! @TomHarrington CloudKit generates the recordID's and i save them locally for identification. Thunk, I receive notifs when a record is deleted, and I want to be able to delete the local version of that object based on that notif. Would any of you know a way? Commented Aug 1, 2017 at 15:02
  • Hey @Joris416, what else are you looking for that my answer doesn't cover? Commented Aug 9, 2017 at 18:53

2 Answers 2

4

If CloudKit doesn't give you any indication of the type of record that was deleted, it's kind of a pain to deal with. You can't delete objects in Core Data without knowing the entity type, so if CloudKit doesn't give you any clues then you need to check every entity that could have the recordId.

The delete process is the same as usual with Core Data. Do a fetch request with a predicate of something like `recordId = %@" to find a matching object. If you find one, delete it. Except that you have to repeat this for each potential entity.

One approach that might help is to store the recordId in a new, separate entity. Create a new entity called something obvious like CKRecordInfo and keep the recordId there. Every other entity that has CloudKit info would have a one-to-one relationship to this entity. With this setup you'd fetch an instance of the new CKRecordInfo entity, and delete whichever object it's related to.

At the same time though-- I haven't used CloudKit, and it's more than a little surprising that it would give you just the recordId with no information about the record type. Getting the info from the notification would be ideal, if it's possible.

Sign up to request clarification or add additional context in comments.

1 Comment

I guess this is the most conclusive answer for the options that are available now, although I find it really clumsy that apple won't give a recordType with records that are deleted. I tried adding the recordType field to the preferred recordFields, but that just returned an error stating that it can't find that field. Another relationship would mean that I'd have to perform another fetch request per notification and that isn't efficient either, so I just iterate through all my recordTypes until I find the right one for the time being. Thanks!
3

Based on your clarification in the comments, I suggest configuring the .recordFields dictionary when you create the subscription. You can pass a limited amount of info in this dictionary, such as the record type. When you receive the deletion notification, you can unpack the recordFields from the notification object.

You can find more info in Apple's docs at https://developer.apple.com/documentation/cloudkit/ckquerynotification/1428114-recordfields

Update

Here's how I do it. I use objective-C, so you'll have to sort out the SWIFT syntax. But the steps are:

Create an array of records I want sent in the notif

Create a subscription

Create a notificationInfo object

Add my desired keys array to the notificationInfo object

Create the sub using a CKModifySubscriptionsOperation

NSArray *desiredKeys = @[fieldname1, fieldname1, fieldname1];
CKQuerySubscription *subscription = [[CKQuerySubscription alloc] initWithRecordType:recordName
                                                                          predicate:searchConditions
                                                                     subscriptionID:subscriptionID
                                                                            options:fireOn];

CKNotificationInfo *notificationInfo = [CKNotificationInfo new];
notificationInfo.shouldBadge = shouldBadge;
notificationInfo.desiredKeys = desiredKeys;

subscription.notificationInfo = notificationInfo;

CKModifySubscriptionsOperation *subOp = [[CKModifySubscriptionsOperation alloc] initWithSubscriptionsToSave:subsToCreate subscriptionIDsToDelete:subsToDelete];
subOp.modifySubscriptionsCompletionBlock = ^(NSArray<CKSubscription *> *savedSubscriptions,
                                             NSArray<NSString *> *deletedSubscriptionIDs,
                                             NSError *operationError)
{
   //do whatever
}

subOp.database = database;      //set to either public or private DB
[myQueue addOperation:subOp];

When you receive a notification, you just pull the objects back out of the notificationInfo:

    NSString *value1 = [queryNotification.recordFields objectForKey:fieldname1];

If it won't let you actually add the recordType, then you may have to create a custom field with some indicator of the record type, and then pass that as I described above, or fetch the record in question by using the recordID you received in the notification.

1 Comment

hi! sorry for the late response.. I have tried implementing your solution which really gave me hope, but iCloud gives me an error saying it cannot find the field 'recordType'. The documentation of apple does not seem to provide what I should enter for their keys, so would you have any idea what the right key would be?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.