I'm seeing a critical issue where a custom CKRecordZone is consistently deleted server-side when a second iCloud account interacts with a zone-wide CKShare. I've reproduced this 20+ times across two days and have exhausted every client-side fix I can think of. Looking for guidance on what might be going wrong.
Setup
- Container:
iCloud.com.cohencooks(production app on App Store) - Custom
CKRecordZonein owner's private database - Zone-wide
CKShare(recordZoneID:)(iOS 15+ zone sharing) - SwiftData with
ModelConfiguration(cloudKitDatabase: .none)— no automatic CloudKit mirroring - Acceptance via
CKFetchShareMetadataOperation→CKContainer.accept(metadata)(no UICloudSharingController)
Minimal reproduction
// 1. Owner creates zone + share
let zone = CKRecordZone(zoneName: "MyZone")
try await privateDB.save(zone)
let share = CKShare(recordZoneID: zone.zoneID)
share[CKShare.SystemFieldKey.title] = "My Share" as CKRecordValue
share.publicPermission = .readWrite
let (results, _) = try await privateDB.modifyRecords(saving: [share], deleting: [])
// 2. Owner pushes ~500 records to zone — all succeed
// 3. Second user (different iCloud account) accepts share
let metadata = try await container.shareMetadata(for: shareURL)
try await container.accept(metadata)
// 4. Owner's next CKFetchRecordZoneChangesOperation → zoneNotFound (code 26)
// Zone is permanently gone. allRecordZones() confirms deletion.
What I observe
Three distinct failure patterns depending on configuration:
Pattern 1 — publicPermission = .readWrite, no addParticipant:
Zone dies instantly after acceptance. First push notification shows cloudkit.share changed (zone alive), second push notification returns zoneNotFound. The non-owner never successfully wrote anything.
Pattern 2 — publicPermission = .none with explicit addParticipant:
Zone survives acceptance and 2-3 minutes of bidirectional sync (non-owner pulls 578 records, pushes meal plans back). Then a push notification arrives and the zone is gone. This is dramatically better than Pattern 1 but still fails.
Pattern 3 — Container destabilization after repeated testing: After 20+ create/delete cycles in one day, zones die from the owner's own push notifications — no second device involved at all. The container appears to enter an unstable state.
Inconsistent state after deletion
Here's something that might help narrow this down. After one of the zone deletions, I deployed the same build to a second device signed into a different iCloud account that had previously accepted the CKShare. Without sending a new invite, that device found the "Household" zone via allRecordZones() on sharedCloudDatabase — it could pull all 578 records, push updates, and the share URL still resolved.
Meanwhile, the owner device (zone creator) gets "zone not found" from both allRecordZones() and direct recordZone(for:) on privateCloudDatabase.
So it looks like the zone is deleted from the owner's private database, but the CKShare and zone records remain accessible to participants via the shared database. Participants can still read and write as if nothing happened — the owner just can't see the zone anymore.
This also creates a recovery problem — when the owner creates a new zone with the same name, it gets a new CKShare URL, but the participant is still connected to the old "ghost" zone. The two sides are permanently split.
Does this mean the zone deletion is happening through a path that doesn't properly clean up the sharing infrastructure? Is this expected behavior when a zone-wide CKShare's zone is deleted, or does it suggest the deletion is happening through an abnormal server-side path?
What I've ruled out
publicPermission = .readWrite | Changed to .none + explicit addParticipant | Zone survived longer but still eventually deleted |
| Zone name tombstoning | Tested 6 fresh names never used in this container | All eventually deleted |
| Non-owner writes causing deletion | Gated ALL non-owner push methods (recipe, meal plan, grocery, photo, event) | Zone still deleted |
database.save(share) vs modifyRecords | Switched to modifyRecords(saving:deleting:) | Zone still deleted |
NSPersistentCloudKitContainer interference | Removed all Core Data CloudKit code | Zone still deleted |
| Double share acceptance | Fresh app install, single acceptance only | Zone still deleted |
| Advanced Data Protection | Neither account has ADP enabled | Not the cause |
| Programmatic vs system acceptance | Tested both container.accept() and tapping share link | Zone still deleted |
CloudKit Dashboard
No ZoneDelete operation is visible in the logs. All operations are ZoneFetch, ZoneChanges, RecordQuery, RecordFetch. I do see EphemeralGroup operations targeting the custom zone — not sure what generates those.
Comparison with working apps
I compared my implementation with another app that uses the exact same zone-wide CKShare(recordZoneID:) pattern with publicPermission = .readWrite and programmatic acceptance — and it works. The main difference is that app uses CKSyncEngine (iOS 17+) rather than raw CKFetchRecordZoneChangesOperation / CKModifyRecordsOperation. Could CKSyncEngine be handling something internally that prevents this issue?
Questions
- Is there a known interaction between zone-wide
CKShare(recordZoneID:)acceptance and zone lifecycle that could cause zone deletion? - Does
CKSyncEnginehandle zone-wide sharing differently than manualCKFetchRecordZoneChangesOperation+CKModifyRecordsOperation? - What generates
EphemeralGroupoperations in CloudKit Dashboard? Could these trigger a zone delete? - After 20+ zone create/delete cycles in a container, is there a server-side rate limit or tombstone mechanism that would destabilize new zones?
- Is the inconsistent state I described (zone gone from owner's private DB but still accessible from participant's shared DB) expected behavior, or does it indicate the deletion is happening through an abnormal path?
- Is the custom programmatic acceptance flow (
CKFetchShareMetadataOperation→container.accept()) fully supported for zone-wide shares, or does it requireUICloudSharingController?
Any guidance would be greatly appreciated. This is blocking multi-user functionality for our app (mesa, a meal planning app on the App Store). Single-user sync works perfectly — the issue only manifests when a second iCloud account is involved.
Environment: iOS 18.4.1, Xcode 16+, Swift, SwiftUI
Let's follow up with your other post, which I've replied.
Best,
——
Ziqiao Chen
Worldwide Developer Relations.