Post

Replies

Boosts

Views

Activity

Core Data modifications not saved in two of three tables
I'm a bit lost because of a problem I never experienced before: I create entries in 3 Core Data tables and link them. As long as the app is open, everything is fine, I can see the database entries in the three tables. Once the App is closed and restarted, however, the new entries in two of the three tables are gone. I use Core Data for data storage and DB Browser for SQLite for inspecting the database running in the Simulator. Here's the relevant function where all Core Data handling happens: /** Creates a new ComposedFoodItem from the ComposedFoodItemViewModel. Creates the related FoodItem and the Ingredients. Creates all relationships. - Parameter composedFoodItemVM: The source view model. - Returns: A Core Data ComposedFoodItem; nil if there are no Ingredients. */ static func create(from composedFoodItemVM: ComposedFoodItemViewModel, generateTypicalAmounts: Bool) -> ComposedFoodItem? { debugPrint(AppDelegate.persistentContainer.persistentStoreDescriptions) // The location of the .sqlite file let moc = AppDelegate.viewContext // Create new ComposedFoodItem (1) let cdComposedFoodItem = ComposedFoodItem(context: moc) // No existing composed food item, therefore create a new UUID cdComposedFoodItem.id = UUID() // Fill data cdComposedFoodItem.amount = Int64(composedFoodItemVM.amount) cdComposedFoodItem.numberOfPortions = Int16(composedFoodItemVM.numberOfPortions) // Create the related FoodItem (2) let cdFoodItem = FoodItem.create(from: composedFoodItemVM, generateTypicalAmounts: generateTypicalAmounts) // Relate both (3) cdComposedFoodItem.foodItem = cdFoodItem // Add cdComposedFoodItem to composedFoodItemVM composedFoodItemVM.cdComposedFoodItem = cdComposedFoodItem // Add new ingredients (4) if let cdIngredients = Ingredient.create(from: composedFoodItemVM) { cdComposedFoodItem.addToIngredients(NSSet(array: cdIngredients)) // Save new composed food item try? moc.save() // Return the ComposedFoodItem return cdComposedFoodItem } else { // There are no ingredients, therefore we delete it again and return nil moc.delete(cdComposedFoodItem) try? moc.save() return nil } } What the function does: Creates a new entry in table ComposedFoodItem Creates another new entry in another table FoodItem Relates both entries Creates another 1..n entries in a third table Ingredient and links these to the entry created in step 1 All this works fine, I can see all relations and entries in the database. Then I quit and restart the app. The entry created in step 2 is still there, but the entries created in steps 1 and 4 are gone, as well as the relationships (of course). My suspicion: I recently implemented a Core Data migration from Data Model version 1 ("EasyFPU") to version 2 ("EasyFPU 2"). In this migration, I have two custom migration policies for exactly the two tables, which are not stored. The migration policies are pretty simple (and identical for both tables): /** No Ingredient is created in the destination model, i.e., there will be no Ingredients */ override func createDestinationInstances(forSource sourceIngredient: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws { // Do nothing on purpose debugPrint("Not migrating Ingredient with ID: \((sourceIngredient as? Ingredient)?.id.uuidString ?? "unknown")") } And what I suspect is, that this migration policies are somehow called when restarting the app, but I have no idea why, because the migration has already happened before. If I set a breakpoint in the debugPrint line of the code snippet above, I actually never reach this breakpoint - as expected. Nevertheless are the two tables Ingredient and ComposedFoodItem empty after restart. My AppDelegate Core Data persistentContainer variable looks like this: lazy var persistentContainer: NSPersistentCloudKitContainer = { let container = NSPersistentCloudKitContainer(name: "EasyFPU") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) return container }() I tried to replace "EasyFPU" with "EasyFPU 2", but this apparently is not the version, but the container name. Any idea? Thanks in advance!
1
0
169
Sep ’24
Core Data modifications not saved in two of three tables
I'm a bit lost because of a problem I never experienced before, however, I have a suspicion. I use Core Data for data storage and DB Browser for SQLite for inspecting the database running in the Simulator. Here's the relevant function where all Core Data handling happens: /** Creates a new ComposedFoodItem from the ComposedFoodItemViewModel. Creates the related FoodItem and the Ingredients. Creates all relationships. - Parameter composedFoodItemVM: The source view model. - Returns: A Core Data ComposedFoodItem; nil if there are no Ingredients. */ static func create(from composedFoodItemVM: ComposedFoodItemViewModel, generateTypicalAmounts: Bool) -> ComposedFoodItem? { debugPrint(AppDelegate.persistentContainer.persistentStoreDescriptions) // The location of the .sqlite file let moc = AppDelegate.viewContext // Create new ComposedFoodItem (1) let cdComposedFoodItem = ComposedFoodItem(context: moc) // No existing composed food item, therefore create a new UUID cdComposedFoodItem.id = UUID() // Fill data cdComposedFoodItem.amount = Int64(composedFoodItemVM.amount) cdComposedFoodItem.numberOfPortions = Int16(composedFoodItemVM.numberOfPortions) // Create the related FoodItem (2) let cdFoodItem = FoodItem.create(from: composedFoodItemVM, generateTypicalAmounts: generateTypicalAmounts) // Relate both (3) cdComposedFoodItem.foodItem = cdFoodItem // Add cdComposedFoodItem to composedFoodItemVM composedFoodItemVM.cdComposedFoodItem = cdComposedFoodItem // Save before adding Ingredients, otherwise this could lead to an NSInvalidArgumentException (4) try? moc.save() // Add new ingredients (5) if let cdIngredients = Ingredient.create(from: composedFoodItemVM) { cdComposedFoodItem.addToIngredients(NSSet(array: cdIngredients)) // Save new composed food item try? moc.save() // Return the ComposedFoodItem return cdComposedFoodItem } else { // There are no ingredients, therefore we delete it again and return nil moc.delete(cdComposedFoodItem) try? moc.save() return nil } } What the function does: Creates a new entry in table ComposedFoodItem Creates another new entry in another table FoodItem Relates both entries Saves the modifications (and as of here I can see both new entries in the DB with all relations created correctly) Creates another 1..n entries in a third table Ingredient and links these to the entry created in step 1 All this works fine, I can see all relations and entries in the database. Then I quit and restart the app. The entry created in step 2 is still there, but the entries created in steps 1 and 5 are gone, as well as the relationships (of course). My suspicion: I recently implemented a Core Data migration from Data Model version 1 ("EasyFPU") to version 2 ("EasyFPU 2"). In this migration, I have two custom migration policies for exactly the two tables, which are not stored. The migration policies are pretty simple (and identical for both tables): /** No Ingredient is created in the destination model, i.e., there will be no Ingredients */ override func createDestinationInstances(forSource sourceIngredient: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws { // Do nothing on purpose debugPrint("Not migrating Ingredient with ID: \((sourceIngredient as? Ingredient)?.id.uuidString ?? "unknown")") } And what I suspect is, that this migration policies are somehow called when restarting the app, but I have no idea why, because the migration has already happened before. If I set a breakpoint in the debugPrint line of the code snippet above, I actually never reach this breakpoint - as expected. Nevertheless are the two tables Ingredient and ComposedFoodItem empty after restart. My AppDelegate Core Data persistentContainer variable looks like this: lazy var persistentContainer: NSPersistentCloudKitContainer = { let container = NSPersistentCloudKitContainer(name: "EasyFPU") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) return container }() I tried to replace "EasyFPU" with "EasyFPU 2", but this apparently is not the version, but the container name. Any idea? Thanks in advance!
0
0
303
Jan ’24