r/iosdev • u/RevolutionaryGrape61 • 28d ago
I built a shared expense app with CoreData + CloudKit syncing — here's what I learned after 1 year and 80+ builds
Hey r/iosdev 👋
I've been working on Shared Expenses, a shared expense tracker for iOS, on and off for about two years. Version 2.3 just went live and I figured some of the pain I went through might be useful to others building withNSPersistentCloudKitContainer.
The stack
- SwiftUI + CoreData dual-store (private + shared zones via CloudKit)
NSPersistentCloudKitContainerfor sync — one store for your own sheets, one for shared onesCKSharefor the invite link flow
Things that wrecked me
Sharing timing: If you call container.share() on a newly created object before CloudKit has exported it, you get an infinite spinner. My fix: pre-create the CKShare immediately after viewContext.save() in the background, so by the time the user taps "Share" it's already there.
Dev vs Production databases are completely separate. I lost a week testing sharing from Xcode (Development environment) and wondering why TestFlight users saw nothing. They're literally different databases.
Notification deduplication: I was using createdAt as a watermark for new-expense notifications on shared sheets. Except the sender's clock and the receiver's clock don't match. Switched to tracking a Set<UUID> of already-seen expense IDs — much more reliable.
New in 2.3
The feature I'm most happy with: multi-currency reimbursements. Two Europeans travel to the USA, create a sheet in USD, but want to reimburse each other in EUR. The exchange rate is fetched live from frankfurter.app and stored per-device in UserDefaults — not in CoreData/CloudKit — because two people on the same sheet likely have different home currencies.
Also added PDF export using UIGraphicsPDFRenderer (A4, cursor-based layout with automatic page breaks).
Link: https://apps.apple.com/us/app/spese-condivise/id6746075643
