CloudKit - moving record objects between zones

My app has three main SwiftData models: Collection, SavedItem, and Extract.

  • A Collection can contain subcollections (folders within folders) and SavedItems (files).
  • Each SavedItem can have child Extracts.

I'm preparing for the ability for users to be able to share Collections with each other.

Currently, my architecture treats each Collection as the root of its own CloudKit zone (a root parent Collection and all of its items and subcollections live in 1 zone).

This makes sharing and isolation straightforward, but it also means that moving a SavedItem or subcollection between Collections involves moving it across zones.

I’m trying to figure out the best pattern for handling these cross-zone moves while keeping data integrity, relationships, and sharing intact.

My understanding is that in CloudKit, and moving a record from Zone A to Zone B would require deleting it from Zone A and recreating it in Zone B - while somehow maintaining the link back to my local SwiftData store.

Has anyone run into this or know how best I should handle it?

Answered by DTS Engineer in 864692022

If you are using SwiftData + CloudKit, which is based on NSPersistentCloudKitContainer, it doesn't support cross-share relationships, and so you can't have a collection in one zone and a relationship of the collection (a sub-collection, for example) in another. This is mentioned in here:

" NSPersistentCloudKitContainer doesn’t support cross-share relationships. That is, it doesn’t allow relating objects associated with different shares. When sharing an object, NSPersistentCloudKitContainer moves the entire object graph, which includes the object and all its relationships, to the share’s record zone. "

If you are using the CloudKit framework directly, the source and target objects of CKRecord.Reference must be in the same zone of the same database, and so you will need to maintain the relationship with your own logic.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Adding some more context to this as I'm determining how best to set up my CloudKit zones:

Option A (Current) - 1 zone per Root parent collection Option B - 1 zone per collection

With Option A, I’d need to handle cross-zone migration whenever a SavedItem moves between Collections, or when a Collection’s parent changes (since that effectively moves an entire sub-tree of data into a new/different zone).

With Option B, where each Collection has its own zone, I’d only need to worry about migrating SavedItems and Extracts when they move between Collections - parent/child changes between Collections themselves would just update references.

Trade-off becomes:

  • Option A → Simpler sharing (since sharing a single Collection would automatically include all its subcollections), but I’d need zone-migration logic for all object types — Collections, SavedItems, and Extracts.
  • Option B → I’d still need zone-migration logic for SavedItems and Extracts, but not for Collections. However, sharing a Collection that has subcollections would require creating multiple CKShares.

If you are using SwiftData + CloudKit, which is based on NSPersistentCloudKitContainer, it doesn't support cross-share relationships, and so you can't have a collection in one zone and a relationship of the collection (a sub-collection, for example) in another. This is mentioned in here:

" NSPersistentCloudKitContainer doesn’t support cross-share relationships. That is, it doesn’t allow relating objects associated with different shares. When sharing an object, NSPersistentCloudKitContainer moves the entire object graph, which includes the object and all its relationships, to the share’s record zone. "

If you are using the CloudKit framework directly, the source and target objects of CKRecord.Reference must be in the same zone of the same database, and so you will need to maintain the relationship with your own logic.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

CloudKit - moving record objects between zones
 
 
Q