Sharing all container content

I've understood that SwiftData is not abled to share the whole content of a cloudkit database.

So I'm trying to rewrite everything. Does someone knows id Sharing is coming on SwiftData at WWDC 26?

Anyway, can someone can point me an example a a configured coredata stack that share all its content with other icloud users (with sharing pane and accept invitation code).

At this step, on the owner side, I see some data in the default zone of my private container but nothing is visible on the shared zone. Maybe I don't understand where and when I should check shared data in cloudkit console. Need Help also here.

See below by configuration stack:

   // Core Data container
    public lazy var container: NSPersistentContainer = {
        switch delegate.usage() {
        case .preview : return previewContainer()
        case .local : return localContainer()
        case .cloudKit : return cloudKitContainer()
        }
    }()

   private func cloudKitContainer() -> NSPersistentContainer {
        
        let modelURL = delegate.modelURL()
        let modelName = modelURL.deletingPathExtension().lastPathComponent

        guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
            fatalError("Could not load Core Data model from \(modelURL)")
        }
                
        let container = NSPersistentCloudKitContainer(
            name: modelName,
            managedObjectModel: model
        )
        
        let groupIdentifier = AppManager.shared.groupIdentifier
        guard let appGroupURL = FileManager.default.containerURL (
            forSecurityApplicationGroupIdentifier: groupIdentifier
        ) else {
            fatalError("App Group not found: \(groupIdentifier)")
        }
        
        // MARK: - Private Store Configuration
        let privateStoreURL = appGroupURL.appendingPathComponent("\(modelName).sqlite")
        let privateStoreDescription = NSPersistentStoreDescription(url: privateStoreURL)
        
        // Persistent history tracking (MANDATORY)
        privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
        
        // CloudKit options for private database
        // Core Data automatically uses the default zone: com.apple.coredata.cloudkit.zone
        let privateCloudKitOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: delegate.cloudKitIdentifier())
        privateCloudKitOptions.databaseScope = .private
        privateStoreDescription.cloudKitContainerOptions = privateCloudKitOptions
        
        // MARK: - Shared Store Configuration
        
        guard let sharedStoreDescription = privateStoreDescription.copy() as? NSPersistentStoreDescription else {
            fatalError("Create shareDesc error")
        }
        
        // The shared store receives zones that others share with us via CloudKit's shared database
        sharedStoreDescription.url = appGroupURL.appendingPathComponent("\(modelName)-shared.sqlite")
        
        // Persistent history tracking (MANDATORY)
        sharedStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        sharedStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
        
        // CloudKit options for shared database
        // This syncs data from CloudKit shared zones when we accept share invitations
        let sharedCloudKitOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: delegate.cloudKitIdentifier())
        sharedCloudKitOptions.databaseScope = .shared
        sharedStoreDescription.cloudKitContainerOptions = sharedCloudKitOptions
        
        // Configure both stores
        // Private store: com.apple.coredata.cloudkit.zone in private database
        // Shared store: Receives shared zones we're invited to
        container.persistentStoreDescriptions = [privateStoreDescription, sharedStoreDescription]

        container.loadPersistentStores { storeDescription, error in
            
            if let error = error as NSError? {
                fatalError("DB init error:\(error.localizedDescription)")
            } else if let cloudKitContiainerOptions = storeDescription.cloudKitContainerOptions {
                switch cloudKitContiainerOptions.databaseScope {
                case .private:
                    self._privatePersistentStore = container.persistentStoreCoordinator.persistentStore(for: privateStoreDescription.url!)
                case .shared:
                    self._sharedPersistentStore = container.persistentStoreCoordinator.persistentStore(for: sharedStoreDescription.url!)
                default:
                    break
                }
            }
            
            let scope = storeDescription.cloudKitContainerOptions?.databaseScope == .shared ? "shared" : "private"
            print("✅ \(scope) store loaded at: \(storeDescription.url?.path ?? "unknown")")
        }
        
        // Auto-merge
        container.viewContext.automaticallyMergesChangesFromParent = true
        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        
        do {
            try container.viewContext.setQueryGenerationFrom(.current)
        } catch {
            fatalError("Fail to pin viewContext to the current generation:\(error)")
        }
        
        return container
    }

Answered by DTS Engineer in 878048022

You are right that there is no way to share a ClodKit database. An owner can only share a custom record zone in their private database. The default record zone doesn’t support sharing, mentioned here.

To share "the whole content of a cloudkit database," the following may be worth considering :

  • When an owner shares a custom record zone, all the data in the record zone is shared, and so you an share your dataset by putting the dataset into the zone.

  • All your app users have access to the public database of your CloudKit container, and you can build your own user management system to control the access. Based on this, you might be able to "share" your data by using the public database instead.

To get more information about CloudKit sharing, you might go through this sample code.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

You are right that there is no way to share a ClodKit database. An owner can only share a custom record zone in their private database. The default record zone doesn’t support sharing, mentioned here.

To share "the whole content of a cloudkit database," the following may be worth considering :

  • When an owner shares a custom record zone, all the data in the record zone is shared, and so you an share your dataset by putting the dataset into the zone.

  • All your app users have access to the public database of your CloudKit container, and you can build your own user management system to control the access. Based on this, you might be able to "share" your data by using the public database instead.

To get more information about CloudKit sharing, you might go through this sample code.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I've already played with the samples and was able to share one by one or a set of records. BUT, let me rephrase, I have a set of entities that I want to share ONCE between invited users with ONE sharing at the top. Could you confirm that is it possible?

If Yes, there is probably one missing step or misunderstood in the following code. In my understanding, based on the samples, to do this, I'm doing :

1/ on owner and participant : configure a NSPersistentCloudKitContainer with inside a databaseScope = .private store AND a databaseScope = .shared store

2/ on owner before presenting the UICloudSharingController:

2.1/ create a shared zone-wide share ID

let shareRecordID = CKRecord.ID(recordName: CKRecordNameZoneWideShare, zoneID: zoneID)

2.2/ create or get an existing CKShare with:

func getOrCreateShare() async throws -> CKShare {
  let zoneID = CKRecordZone.ID(zoneName: sharedZoneName)
        
  // Zone-wide share ID
  let shareRecordID = CKRecord.ID(
    recordName:     CKRecordNameZoneWideShare,
    zoneID: zoneID
  )
        
  // Try to fetch existing share
  do {
    let existingShare = try await privateDatabase.record(for: shareRecordID) as? CKShare
    if let share = existingShare {
      print("Existing share retrieved")
      return share
     }
  } catch let error as CKError where error.code == .unknownItem {
                                    
  print("No existing share, creating new one for the entire zone")
  let share = CKShare(recordZoneID: zoneID)
            share[CKShare.SystemFieldKey.title] = "My Sharing Title" as CKRecordValue
            share[CKShare.SystemFieldKey.shareType] = "net.megy.stokk.inventory" as CKRecordValue
            share.publicPermission = .none  // Private only
            
  guard let savedShare = try await privateDatabase.save(share) as? CKShare else {
                throw CKError(.unknownItem)
            }
            
  print("Return new share created for entire zone")
            return savedShare
        }
  // should  not be reached
  throw CKError(.unknownItem)
 }

3/ on the participant side, that I've plugged my SceneDelegate windowScene(_ windowScene:userDidAcceptCloudKitShareWith:) to accep the invitation with:

public func acceptShare(metadata: CKShare.Metadata) async throws {
        try await CKContainer(identifier: cloudKitIdentifier).accept(metadata)
        print("Invitation accepted, syncing...")
    }

I feel like all entities are not routed to my shared zone. Maybe something missing in my xcdatamodeld file.

Sharing all container content
 
 
Q