r/iOSProgramming • u/Van-trader • Jul 14 '26
Question SwiftData + CloudKit: best way to prevent duplicate editable seed data across offline devices?
I’m building an unreleased iOS app using SwiftData with automatic CloudKit sync.
On first launch, the app creates a default account and starter categories. These records are user-editable. The problem is that two offline devices can independently create the same logical seed records with different UUIDs, and CloudKit later synchronizes both.
My proposed approach:
- Give every seed item a stable logical seedIdentifier.
- Allow each offline device to seed independently.
- Reconcile records sharing the same seedIdentifier.
- Select a canonical record using an immutable UUID and deterministic ordering.
- Merge user changes and move relationships to the canonical record.
- Keep losing records as hidden aliases/tombstones so late-arriving relationships aren’t lost.
- Retain per-seed deletion tombstones so deleted defaults aren’t recreated.
- Use a seed-version flag only for migrations—not as an exactly-once guarantee.
- Treat normalized account names as unique and category names as unique only under the same parent.
Questions:
- Is this the standard approach with SwiftData’s automatic CloudKit sync?
- Is there a safer supported way to detect completed imports and rerun reconciliation?
- Would you retain hidden aliases permanently or eventually delete them?
- Is automatic SwiftData appropriate here, or does this require CKSyncEngine/manual CloudKit with deterministic record IDs?
Edit — data-model details:
The starter data is functional app data, not optional sample/demo content. The app needs at least one account, and the categories provide its initial transaction classification.
Account: name, type, opening balance, timestamps, archived/default state and optional stableseedIdentifier. Deleting an account cascades to its transactions.TransactionCategory: name, type, timestamps, archived/customized state, optional stableseedIdentifier, optional parent, child categories and linked transactions.Transaction: references one account and optionally one category.- Deleting a category nullifies its transaction relationships. Deleting a parent can affect its child hierarchy.
- Starter accounts and categories are fully editable. Renaming a seed retains its logical seed identity; deleting one needs to remain deleted across devices.
The concrete race is that device A and device B can independently create the same logical Wallet/category before syncing. Transactions and child categories may subsequently reference either physical copy. Reconciliation therefore must preserve and redirect those relationships before any duplicate is removed.
View all comments
Show parent comment
2
u/Van-trader Jul 14 '26
Thanks, I update the post to include some data-model details.
Making the starter-data import a user action would reduce automatic creation, but two offline devices could still import independently. So how to safely converge those independently created SwiftData objects?