Manually define a CKRecord ID for a given NSManagedObject instance

I have an app that is already running with CoreData, and I want to allow the users to upload their data to iCloud so, in the case they need to delete their apps or change devices, they don't lose it.

Every time the app is opened, there is a synchronization that happens between CoreData and a JSON file fixture, in order to fill the app with its base values (creating NSManagedObject instances from the aforementioned fixture).

Before the iCloud sync, the CoreData model had some constraints for different entities, to enforce uniqueness, but these had to be stripped since CloudKit doesn't support them. Most of these constraints were basically ids that came from the JSON and represent an item in our Firebase database.

Given that, I want to make the underlying CKRecord.id the same as these ids, so I can avoid the situation where, if a person open the app in a second device, the fixture data is repeated, since the fixture runs before the sync with the iCloud happens.

Is that possible? Any help would be appreciated.

Accepted Reply

hi,

since you are using NSPersistentCloudKitContainer, you might want to take a look at Apple's page on Linking Data Between Two Core Data Stores.

this provides a mechanism so that you can load up what i think you are calling "base values" in a local store, separate from the store that is mirrored to iCloud. this would solve the problem of loading base values on one machine, moving them into the cloud, and then trying to figure out on a second device how to prevent them from being reloaded from iCloud.

hope that helps, DMG

  • The formatting on the reply was really bad, I'll post it again as an answer, so it is better formatted.

Add a Comment

Replies

I’m trying build live stream app I need help app like live me or yubo

Can you email me

Are you doing the synchronization manually? You can add your own CKRecordID when you first create the CKRecord previously to save it to CK. Once the record lives in CK, you can't edit the ID (as far as I know).

Just remember that the ID must be unique between all the CKRecord in your CKRecordZone.

  • Hi iturrajoel, first of all thanks for the help.

    Actually my synchronization is happening automatically, since I'm using NSPersistentCloudKitContainer as my persistentContainer. I imagine that doing it manually would allow me to create each id, but the amount of work needed to do the manual sync would be enormous. Do you think that is the only way to do it, or is there another way to guarantee uniqueness that has a better tradeoff? Like some sort of collision check on the cloud kit update notification, or something like that.

  • I can't help you there, I haven't use automatic synchronization because I need control about what is being synchronized. Actually I'm doing some test to stop using coredata references, so I won't have problems when synchronizing data out of order.

    You should make a list of pros and cons about the two options you have, and then decide which one is less painful, but you will have to compromise at some point.

  • Well, guess that's what I'll do then, hope it works for you too. Thanks for the assistance, really appreciated it :)

Add a Comment

hi,

since you are using NSPersistentCloudKitContainer, you might want to take a look at Apple's page on Linking Data Between Two Core Data Stores.

this provides a mechanism so that you can load up what i think you are calling "base values" in a local store, separate from the store that is mirrored to iCloud. this would solve the problem of loading base values on one machine, moving them into the cloud, and then trying to figure out on a second device how to prevent them from being reloaded from iCloud.

hope that helps, DMG

  • The formatting on the reply was really bad, I'll post it again as an answer, so it is better formatted.

Add a Comment

Hi DMG, I spent the last days implementing this solution to give you a proper feedback. Thank you so much for the direction, I've been able to separate the stores by using fetched properties relating the cloud entities to the local entities, just like the link pointed. But there's something weird happening when I access the fetched properties from a background context.

In my Entity A (EA) I have a fetchedPropertyId of type String that refers to an id property on Entity B (EB). At the same EA I have the fetched property that has a predicate that points to EB and is the following:

id == $FETCH_SOURCE.fetchedPropertyId

The code to fetch the property in EA's class is:

@NSManaged public var fetchedPropertyId: String?

var fetchedProperty: EB? {
    let value = value(forKey: "fetchedProperty")
     
    let fetchedProperties = value as? [EB]
     
    return fetchedProperties?.first
  }

Accessing eaInstance.fetchedProperty from the viewContext returns the correct values (nil if there's no fetchedPropertyId, and the correct item of type EB if fetchedPropertyId is defined).

The weird thing is, whenever I access eaInstance.fetchedProperty in another context, the following error is thrown:

[<NSTemporaryObjectID_default 0x7fe723f48070> valueForUndefinedKey:]: this class is not key value coding-compliant for the key fetchedPropertyId.

This happens if I get the object by getting the objectID from the viewContext's managedObject instance and pass it to backgroundContext.object(with: objectID) and even if I fetch the given object again.

Do you have any idea why is this happening?

  • You did the right thing passing the objectID to the other context, but probably didn't execute its fetch on the context's queue. Wrap that part of your code in backgroundContext.perform { ... } for async, or .performAndWait { ... } for sync execution.

Add a Comment

I'm observing the same issue. Execution of code is performed within backgroundContext.performAndWait{} Accessing the parentObject in the backgroundContext.performAndWait is not an issue here, but as soon as I access fetchedProperties?.first I observe the error stated by @vinicius_likeappro

Did you manage to solve this issue?