Somehow all entries to entities in my database return to their default values, instead of what was saved to them.The top entries are still intact, but the lower entries did reset. The only entries that remain is a binary file.
This happens on macOS 13.4, iOS 16.4.1 and iPadOS 16.4.
Below is how I do all FetchRequests:
class CoreDataStorage: NSObject, ObservableObject{
@Published var tracks: [Track] = []
private var trackController: NSFetchedResultsController<Track>
@Published var reminders: [Reminder] = []
private var reminderController: NSFetchedResultsController<Reminder>
@Published var trekkings: [Trekking] = []
private var trekkingController: NSFetchedResultsController<Trekking>
@Published var donations: [Donation] = []
private var donationsController: NSFetchedResultsController<Donation>
@Published var images: [TrackImage] = []
private var imageController: NSFetchedResultsController<TrackImage>
private let context: NSManagedObjectContext
func getTotalDistance()-> Double{
var total = 0.0
tracks.forEach{ track in
total += track.totalDistance
}
return total / 1000
}
func getTotalTime()-> Double{
var total = 0.0
tracks.forEach{ track in
total += track.totalDuration
}
return total
}
func getSpeed()-> Double {
var max = 0.0
tracks.forEach{ track in
if track.maxSpeed > max{
max = track.maxSpeed
}
}
return max * 3.6
}
func backgroundUpdate(){
DispatchQueue.global(qos: .utility).async {
let speed = self.getSpeed()
let time = self.getTotalTime()
let distance = self.getTotalDistance()
writeWidget(content: widgetContent(min: time, kmh: speed, km: distance))
}
}
init(context: NSManagedObjectContext){
print("CoreDataStorage.init()")
self.trackController = NSFetchedResultsController<Track>(fetchRequest: Track.defaultFetchRequest(), managedObjectContext: context, sectionNameKeyPath: nil, cacheName: "track")
self.reminderController = NSFetchedResultsController<Reminder>(fetchRequest: Reminder.defaultFetchRequest(), managedObjectContext: context, sectionNameKeyPath: nil, cacheName: "reminder")
self.trekkingController = NSFetchedResultsController<Trekking>(fetchRequest: Trekking.defaultFetchRequest(), managedObjectContext: context, sectionNameKeyPath: nil, cacheName: "trekking")
self.donationsController = NSFetchedResultsController<Donation>(fetchRequest: Donation.defaultFetchRequest(), managedObjectContext: context, sectionNameKeyPath: nil, cacheName: "donations")
self.imageController = NSFetchedResultsController<TrackImage>(fetchRequest: TrackImage.defaultFetchRequest(), managedObjectContext: context, sectionNameKeyPath: nil, cacheName: "image")
self.context = context
super.init()
trackController.delegate = self
reminderController.delegate = self
trekkingController.delegate = self
donationsController.delegate = self
imageController.delegate = self
print("fetching CoreData")
do {
try trackController.performFetch()
tracks = trackController.fetchedObjects ?? []
try reminderController.performFetch()
reminders = reminderController.fetchedObjects ?? []
try trekkingController.performFetch()
trekkings = trekkingController.fetchedObjects ?? []
try donationsController.performFetch()
donations = donationsController.fetchedObjects ?? []
try imageController.performFetch()
images = imageController.fetchedObjects ?? []
} catch {
debug.log(error)
}
print("Done!")
}
}
extension CoreDataStorage: NSFetchedResultsControllerDelegate{
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
if controller.fetchRequest.entityName == "Reminder"{
if let reminders = controller.fetchedObjects as? [Reminder]{
self.reminders = reminders
}
}
self.backgroundUpdate()
switch controller.fetchRequest.entityName{
case "Track":
if let tracks = controller.fetchedObjects as? [Track]{
self.tracks = tracks
}
case "Trekking":
if let trekkings = controller.fetchedObjects as? [Trekking]{
self.trekkings = trekkings
}
case "Donation":
if let donations = controller.fetchedObjects as? [Donation]{
self.donations = donations
}
case "TrackImage":
if let images = controller.fetchedObjects as? [TrackImage]{
self.images = images
}
default:
break
}
}
}
And this is how I load the coredatastack
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
CLLocationValueTransformer.register()
let container = NSPersistentCloudKitContainer(name: "Project_GeoTrack")
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
// var new = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.merlijn.project_geotrack")
// new?.appendPathComponent("Project_GeoTrack.sqlite")
// container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: new!)]
// Only initialize the schema when building the app with the
// Debug build configuration.
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), userInfo: \(error.userInfo)")
}
})
#if DEBUG
do {
// Use the container to initialize the development schema.
try container.initializeCloudKitSchema(options: [])
} catch {
// Handle any errors.
print("development schema set-up error")
print(error)
}
#endif
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
And I do not change any entries of the respective coredata entities anywhere else in my code.
I have tried disabling the return faults property in the fetch requests, as well as looking all over the cloud kit dashboard for a setting that might be the culprit.
This is scary as this started happening in production, that's obviously a very bad place to have data integrity failures.
In the uploaded screenshot you can see what an entry should look like, the upper, and what happens to the rest of them. In short: What did I do wrong and how do I prevent this from happening again?
If this is well known mistake, I'm sorry for posting, but I really hope someone can help me out. Thanks in advance for taking the time to read through all this and maybe come up with a solution.