I want to pre-populate the user's database the first time the app is used. This is what I have:
func checkAndPopulateData(modelContext: ModelContext) {
let fetchRequest = FetchDescriptor<PredefItem>()
let existingRecords = try? modelContext.fetch(fetchRequest)
if existingRecords?.isEmpty ?? true {
populatePredefinedData(modelContext: modelContext)
}
}
called in the app:
init() {
checkAndPopulateData(modelContext: sharedModelContainer.mainContext)
}
It works, except when the app is deleted and re-installed, in which case the fetch request doesn't return anything, allowing the items to be inserted, and then it syncs with the cloud, leading to duplicates. Each time the app is deleted and re-installed, a new set of items appears.
How to prevent this? I tried adding unique attributes to the items but it's not supported. I'd prefer anyway not using uniques to solve this as I don't want to trigger errors. Is there a way to wait for CloudKit finishing sync, or any other solution?
Since you're using iCloud, a reliable way to handle this is to use NSUbiquitousKeyValueStore
to track whether the database has already been prepopulated. This allows you to persist a flag in iCloud that indicates whether the database has been initialized, which remains accessible across app reinstalls.
Set a flag after the database is populated for the first time:
After calling populatePredefinedData
, set a boolean in NSUbiquitousKeyValueStore
, like so:
NSUbiquitousKeyValueStore.default.set(true, forKey: "hasPopulatedDatabase")
Check the flag before populating:
Modify your checkAndPopulateData
function to first check the flag:
func checkAndPopulateData(modelContext: ModelContext) {
let hasPopulatedDatabase = NSUbiquitousKeyValueStore.default.bool(forKey: "hasPopulatedDatabase")
if !hasPopulatedDatabase {
populatePredefinedData(modelContext: modelContext)
NSUbiquitousKeyValueStore.default.set(true, forKey: "hasPopulatedDatabase")
}
}
Ensure CloudKit syncs the flag before deciding:
Call NSUbiquitousKeyValueStore.default.synchronize()
to ensure changes are immediately written to iCloud. Synchronization usually happens automatically, but this ensures the data is up-to-date.
If you prefer to avoid relying on NSUbiquitousKeyValueStore
, another solution is to include a unique identifier in your CloudKit records (e.g., an isPredefined
flag or a fixed record ID) to prevent re-creation during subsequent syncs.