Incomplete sync using CloudKit+CoreData and the public database

My app is composed from 2 components: iOS app (public facing) + macOS app (where I will feed in public info).
Both will share the same codebase for Services and Model.

The app will have public content (that will be stored in the public db - and only I will have the option to modify) and private content that each user will be able to create on their private database.

The model is basically simple:
An entity A with primitive fields AF1, AF2, AF3
An entity B with primitive fields BF1, BF2
A OneToMany relation between A and B (A can have many B).

The problematic flow:
I save info to the public db via Mac app (check it via CK dashboard)
I validate the data on the iOS app via a real device (bring app the background, bring app to foreground, wait a couple of seconds and the data is there)
I do various tests like: Adding a new B to A -> save -> check or updating a B -> save -> check.
Most of the time everything works like a charm (obvious the data comes with a delay, but it comes).
The problem is that sometimes when I update via macAPP, I get partial data on the iOS app (e.g. the A table is always updated (by checking the primate fields) and the B relations will never get updated.

Notes:
  • the CK dashboard if up to date each time I trigger a save from Mac app

  • on the iOS app, when the records don't reach the device, I manually check the sqlite DB and I can confirm that it's inconsistent.

  • I blame the coredatastack, but I really didn't get much info for public database setup.

My CoreDataStack is shared on both apps and looks like this:


Code Block let container = NSPersistentCloudKitContainer(name: ckContainerName)
let cloudStoreLocation = storeDirectory.appendingPathComponent("cloud.sqlite")
let cloudStoreDescription = NSPersistentStoreDescription(url: cloudStoreLocation)
cloudStoreDescription.configuration = privateCloudStoreName
cloudStoreDescription.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.ABC")
cloudStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
cloudStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
let publicCloudStoreLocation = storeDirectory.appendingPathComponent("cloud-public.sqlite")
let publicCloudStoreDescription = NSPersistentStoreDescription(url: publicCloudStoreLocation)
publicCloudStoreDescription.configuration = publicCloudStoreName
var cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.ABC")
cloudKitContainerOptions.databaseScope = .public
publicCloudStoreDescription.cloudKitContainerOptions = cloudKitContainerOptions
if isiOSApp { // only iOS will load a private store
container.persistentStoreDescriptions = [cloudStoreDescription, publicCloudStoreDescription]
} else {
container.persistentStoreDescriptions = [publicCloudStoreDescription]
}
container.loadPersistentStores(completionHandler: { (_, error) in
guard let error = error as NSError? else { return }
fatalError("###\(#function): Failed to load persistent stores:\(error)")
})
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.viewContext.transactionAuthor = "appauthname"
container.viewContext.automaticallyMergesChangesFromParent = true
do {
try container.viewContext.setQueryGenerationFrom(.current)
} catch {
fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)")
}
NotificationCenter.default.addObserver(
self, selector: #selector(type(of: self).storeRemoteChange(_:)),
name: .NSPersistentStoreRemoteChange, object: nil)



I looked into cloud-public.sqlite file and I noticed the following:
  • the LASTFETCHDATE from RECORDZONEQUERY is updated, so fetches are performed. )

  • in RECORDMETADATA most of the CKRECORDNAMES are present, but not the one I'm missing

  • CKDATABASEMETADATA is updated -> LASTFETCHDATE is updated.

so I created an update to A, I added 3 new B via the macOS app and on iPhone, by looking at the DB, I get only 2 B, 1 is missing.

What am I missing?
Reproducible example here
I saw in your example you have 4 configurations for the core data. The entities AEntity and BEntity belongs to the Default, Cloud and CloudPublic.

I'm not sure, but I think it can be a problem to have the entries in 3 configurations and one configuration don't use "Used with CloudKit" and the two other using "Used with CloudKit".
Sorry for the late reply, but I don't see another way.

Default - came with the project, I don't see an active minus to remove entities from it.
Cloud - Will be used with CloudKit and the Private DB
CloudPublic - Will be used with CloudKit and the Public DB

Do you see another option to do this?
Incomplete sync using CloudKit+CoreData and the public database
 
 
Q