Reading CloudKit Records for Core Data
Access CloudKit records created from Core Data managed objects.
Overview
Although your Core Data app interacts primarily with managed objects, you can access a managed object’s CKRecord directly. This is useful if you’re leveraging CloudKit to add features like sharing. You can also use CloudKit JS to access CloudKit records from your web app.
To prevent collision with existing CloudKit record types and reserved names, CloudKit prefixes the CKRecord types and fields it creates for your Core Data entities with CD_.
To work with records directly, you need to understand the mappings between entities and record types, attributes and fields, and the ways a record stores relationships.
Read Entities from Record Types
CloudKit doesn’t typically support inheritance, so it provides only a single system field, recordType, to hold type information. Core Data stores the name of the root entity from the inheritance hierarchy in recordType.
When you initialize a schema, Core Data adds a custom field to the record type, CD_entityName, to store the name of the current entity.
[Image]
For example, an entity named Post generates the following structure (before adding its attributes), with its CD_entityName set to Post, and its recordType set to CD_Post.
<CKRecord: 0x7fbae9e19510; recordID=CD_Post_UUID:
(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
"CD_entityName" = Post;
}, recordType=CD_Post>Consider a second entity, ImagePost, that inherits from Post.
[Image]
ImagePost generates the following structure (before adding its attributes), with its CD_entityName set to ImagePost, and its recordType set to CD_Post.
<CKRecord: 0x7f9c9fe17780; recordID=CD_ImagePost_UUID:
(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
"CD_entityName" = ImagePost;
}, recordType=CD_Post>Query against recordType when searching against an entity’s inheritance hierarchy. Query against CD_entityName when searching for instances of a specific type.
For more information about CloudKit queries, see CKQuery.
Read Attributes from Fields
When you initialize a schema, Core Data creates fields for each of an entity’s attributes, mapping the attribute name to a field with a key in the form CD_[attribute.name]. The field’s type may vary between Core Data and CloudKit.
Core Data attribute type |
|
|
|---|---|---|
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| — | not supported |
| — | not supported |
All variable length attribute types—String, Binary Data, and Transformable—generate an additional field with a key in the form CD_[attribute.name]_ckAsset. If a field’s value grows too large to store within the record size limit of 1MB, Core Data automatically converts the value to an external asset. Core Data transitions between the original field and its asset counterpart transparently during serialization. When inspecting a CloudKit record directly, check the length of the original field’s value; if it is zero, look in the asset field.
[Image]
For example, an entity named Post with String content and title attributes would generate the following fully materialized record, with pairs of fields for CD_content and CD_content_ckAsset, and for CD_title and CD_title_ckAsset.
<CKRecord: 0x7f9c9fd0f870; recordID=CD_Post_UUID:
(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
"CD_content" = "An example core data string";
"CD_content_ckAsset" = "<CKAsset: 0x7f9c9fe1db50;
path=/var/folders/*/C9EDC901-385B-4778-9D78-03E9C740AD89.fxd,
UUID=C37985B7-F959-4174-AA93-C404F9DCC6A5>";
"CD_entityName" = Post;
"CD_title" = "An example core data string";
"CD_title_ckAsset" = "<CKAsset: 0x7f9c9fd10140;
path=/var/folders/*/C7977A3A-623E-441E-9086-66F2F5B7B746.fxd,
UUID=81800071-ECBD-46E1-B4F9-2F7168269497>";
}, recordType=CD_PostRead One-to-One Relationships from Fields
One-to-one relationships store foreign keys in both related records, mapping the relationship name to a field with a key in the form CD_[relationship.name]. This field stores the foreign key of the related object in the form CKRecord.recordID.recordName.
For example, consider a one-to-one relationship between an ImageData entity and an Attachment entity. In Core Data, the Attachment has an imageData relationship, and the ImageData has an attachment relationship.
[Image]
This one-to-one relationship between ImageData and Attachment would generate the following CloudKit records.
<CKRecord: 0x7f9ca1300f50; recordID=CD_Attachment_UUID:
(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
"CD_entityName" = Attachment;
"CD_imageData" = "CD_ImageData_UUID";
}, recordType=CD_Attachment>
<CKRecord: 0x7f9c9fc18610; recordID=CD_ImageData_UUID:
(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
"CD_attachment" = "CD_Attachment_UUID";
"CD_entityName" = ImageData;
}, recordType=CD_ImageData>The CD_imageData field on the CD_Attachment contains the foreign key of the image, and the CD_attachment field on the CD_ImageData contains the foreign key of the attachment.
Read One-to-Many Relationships from Fields
One-to-many relationships store a foreign key on each record on the many side of the relationship, mapping the relationship name to a field with a key in the form CD_[relationship name]. This field stores the foreign key of the related object in the form CKRecord.recordID.recordName.
[Image]
For example, a one-to-many relationship between a single Post and multiple Attachment instances would generate multiple CD_Attachment records. Each record contains the foreign key of the Post it belongs to in their CD_post field.
<CKRecord: 0x7f9ca1300f50; recordID=CD_Attachment_UUID:
(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
"CD_entityName" = Attachment;
"CD_post" = "CD_VideoPost_UUID";
}, recordType=CD_Attachment>Generated Post records don’t contain a reference to their attachments.
Read Many-to-Many Relationships from CDMR Records
Many-to-many relationships model the join table using a custom Core Data Mirrored Relationship (CDMR) record type.
CloudKit doesn’t support the notion of a join, and it’s inefficient to encode arrays on both records and keep them in sync. Instead, Core Data constructs a CDMR record to accurately and succintly capture all of the criteria of the join.
CDMR records have the following fields.
CDMR Field | Description |
|---|---|
| An alphabetically sorted, semicolon-separated list of the entities in the relationship, for example, |
| The record names of the two related objects, for example, |
| The relationship names, for example, |
For example, consider a many-to-many relationship between Tag and Post entities.
[Image]
The individual Tag and Post records don’t contain fields for the relationship.
<CKRecord: 0x7f9c9fd0f870; recordID=CD_Post_UUID:
(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
"CD_content" = "An example core data string";
"CD_content_ckAsset" = "<CKAsset: 0x7f9c9fe1db50;
path=/var/folders/*/C9EDC901-385B-4778-9D78-03E9C740AD89.fxd,
UUID=C37985B7-F959-4174-AA93-C404F9DCC6A5>";
"CD_entityName" = Post;
"CD_title" = "An example core data string";
"CD_title_ckAsset" = "<CKAsset: 0x7f9c9fd10140;
path=/var/folders/*/C7977A3A-623E-441E-9086-66F2F5B7B746.fxd,
UUID=81800071-ECBD-46E1-B4F9-2F7168269497>";
}, recordType=CD_Post>
<CKRecord: 0x7f9ca10188d0; recordID=CD_Tag_UUID:
(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
"CD_color" = {length = 17, bytes = 0x536f6d65206578616d706c652064617461};
"CD_color_ckAsset" = "<CKAsset: 0x7f9c9fd07790;
path=/var/folders/*/5D5DF5B2-DB27-4F01-B311-52A274374F59.fxd,
UUID=787F4868-1F4D-4BF7-86D4-3867BEA65172>";
"CD_entityName" = Tag;
"CD_name" = "An example core data string";
"CD_name_ckAsset" = "<CKAsset: 0x7f9ca1300af0;
path=/var/folders/*/C40A1E1F-C2F5-4BA1-A6ED-F5977301A1F7.fxd,
UUID=A4C4B698-55FC-4C87-BA8A-D6DD0011DD90>";
"CD_uuid" = "51BAFD98-D1F7-472F-95D6-BBF40D7CBD75";
}, recordType=CD_Tag>The relationship between any two Tag and Post records exists in a third CDMR record. The CDMR record describes the entity type, record name, and Core Data relationship between the Tag and Post.
<CKRecord: 0x7f9ca1301780; recordID=EE64F478-A761-4049-B559-853457ABA997:
(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
"CD_entityNames" = "Post:Tag";
"CD_recordNames" = "CD_Post_UUID:CD_Tag_UUID";
"CD_relationships" = "tags:posts";
}, recordType=CDMR>The structure of a CDMR record is carefully designed to occupy the minimum necessary footprint, and to require the least effort to decode and work with, making it usable outside the Core Data framework.
Access CloudKit Objects
You can access a managed object’s CKRecord directly through its associated context using record(for:) for a single record, or records(for:) for multiple records. To retrieve the record ID only, use recordID(for:), or recordIDs(for:).
Alternatively, use the class functions recordForManagedObjectID:, recordsForManagedObjectIDs:, recordIDForManagedObjectID:, and recordIDs(for:) on NSPersistentCloudKitContainer.