User A shares zone with User B (influenced from https://github.com/apple/sample-cloudkit-zonesharing, but I have just one zone "Contacts" that I am sharing):
private func shareConfiguration() async throws -> (CKShare, CKContainer) {
let container = CKContainer(identifier: "iCloud.com.***.syncer")
let database = container.privateCloudDatabase
let zone = CKRecordZone(zoneName: "Contacts")
let fetchedZone = try await database.recordZone(for: zone.zoneID)
guard let existingShare = fetchedZone.share else {
print("Does not have existing share")
let share = CKShare(recordZoneID: zone.zoneID)
share[CKShare.SystemFieldKey.title] = "Resources"
_ = try await database.modifyRecords(saving: [share], deleting: [])
return (share, container)
}
print("Has existing share")
guard let share = try await database.record(for: existingShare.recordID) as? CKShare else {
throw NSError(domain: "", code: 0, userInfo: nil)
}
return (share, container)
}
...
let (share,container) = try! await shareConfiguration()
shareView = CloudSharingView(container: container, share: share) // UIViewControllerRepresentable implementation
User B accepts share invitation (borrowed from https://github.com/apple/sample-cloudkit-zonesharing)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
guard cloudKitShareMetadata.containerIdentifier == "iCloud.com.***.syncer" else {
print("Shared container identifier \(cloudKitShareMetadata.containerIdentifier) did not match known identifier.")
return
}
// Create an operation to accept the share, running in the app's CKContainer.
let container = CKContainer(identifier: "iCloud.com.***.syncer")
let operation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
debugPrint("Accepting CloudKit Share with metadata: \(cloudKitShareMetadata)")
operation.perShareResultBlock = { metadata, result in
let shareRecordType = metadata.share.recordType
switch result {
case .failure(let error):
debugPrint("Error accepting share: \(error)")
case .success:
debugPrint("Accepted CloudKit share with type: \(shareRecordType)")
}
}
operation.acceptSharesResultBlock = { result in
if case .failure(let error) = result {
debugPrint("Error accepting CloudKit Share: \(error)")
}
}
operation.qualityOfService = .utility
container.add(operation)
}
}
User B through CKSyncEngine is able to read all records. However, when User B tries to write to database through CKSyncEngine, User B on his device gets following error:
<CKSyncEngine 0x1282a1400> error fetching changes with context <FetchChangesContext reason=scheduled options=<FetchChangesOptions scope=all group=CKSyncEngine-FetchChanges-Automatic)>>: Error Domain=CKErrorDomain Code=2 "Failed to fetch record zone changes" UserInfo={NSLocalizedDescription=Failed to fetch record zone changes, CKPartialErrors={
"<CKRecordZoneID: 0x3024872a0; zoneName=Contacts, ownerName=_18fb98f978ce4e9c207daaa142be6024>" = "<CKError 0x30249ed60: \"Zone Not Found\" (26/2036); server message = \"Zone does not exist\"; op = DC9089522F9968CE; uuid = 4B3432A4-D28C-457A-90C5-129B24D258C0; container ID = \"iCloud.com.***.syncer\">";
}}
Also, in CloudKit console, if I go to Zones, I don't see any zones under Shared Database. Wasn't I supposed to see my zone here?
However, I see "Contacts" zone under Private Database. If I expand Zone details I see following:
Zone wide sharing is enabled. All records in this zone are being shared with the sharing participants below.
And under Participants I see both User A and User B. User B is marked as:
Permission READ_WRITE
Type USER
Acceptance INVITED
What puzzles me is why READ works, but not WRITE?