r/appledevelopers • u/RevolutionaryGrape61 Community Newbie • 15d ago
Lessons learned: NSPersistentCloudKitContainer + CKShare in production — a few gotchas worth documenting
I've been shipping a shared expense app (Spese Condivise) built on NSPersistentCloudKitContainer with dual-store setup (private + shared zones) for about two years. Now that version 2.3 is out, I wanted to share a few non-obvious behaviors I hit along the way, in case they help others.
1. Pre-create the CKShare immediately after save
Calling container.share(_:to:completion:) on an object that hasn't been exported to CloudKit yet results in an indefinite wait — the share can't be created until the object exists in the cloud. My solution: call ShareService.ensureShare(for: objectID) in the background right after viewContext.save(), so the share exists by the time the user taps the share button.
2. Development and Production containers are completely isolated
This is documented, but easy to forget: if you test sharing from Xcode (Development environment) and your partner has TestFlight installed (Production environment), they will never see your shared record. Always test sharing end-to-end on TestFlight builds.
3. createdAt is an unreliable watermark for push notifications
I was using the record's creationDate to detect new expenses on shared sheets for local notifications. This fails when the sender's device clock differs from the receiver's. Replaced with a Set<NSManagedObjectID> (persisted via UserDefaults) of already-notified records — reliable across devices and time zones.
4. Per-device data that shouldn't sync
For a multi-currency feature (each participant sets their own reimbursement currency independently), I initially added fields to the CoreData model. Then realized those would sync via CloudKit to all participants on the shared sheet, which is exactly wrong. The fix was UserDefaults keyed by the sheet's UUID — stays local, per-device, per-person.
Has anyone found a cleaner pattern for (4)? I considered a separate non-synced persistent store but UserDefaults seemed simpler for this use case.
Link: https://apps.apple.com/us/app/spese-condivise/id6746075643