I've come across an issue which rarely happens, and (of course) it works perfectly when I test it myself. It has only happened for a few users, and I know I have at least a couple hundred who use the same App daily.
The issue
When updating a list of coredata objects in a tableview, it not only updates the objects (correctly), it also creates duplicates of all these objects.
Coredata setup
It's a NSPersistentCloudKitContainer with these settings: container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
Tableview setup
I have a tableview which displays a list of the 'ActivityType' objects. It's very simple, they have a name (some other basic string/int properties), and an integer called 'index'. This 'index' exists so that users can change the order in which they should be displayed. Here is some code for how each row is setup:
for activityType in activityTypes {
row = BlazeRow()
row.title = activityType.name
row.cellTapped = {
self.selectedActivityType(activityType)
}
row.object = activityType
row.cellReordered = {
(index) in
self.saveNewOrder()
}
section.addRow(row)
}
As you can see, it has 2 methods. One for selecting the activity which shows its details in a new viewcontroller, and one which is called whenever the order is changed.
Here's the method that is called whenever the order is changed:
func saveNewOrder() {
Thread.printCurrent()
let section = self.tableArray[0] as! BlazeSection
for (index, row) in section.rows.enumerated() {
let blazeRow = row as! BlazeRow
let object = blazeRow.object as! ActivityType
object.index = Int32(index)
}
BDGCoreData.saveContext()
}
And here's the code that saves the context (I use a singleton to easily access the viewcontext):
class func saveContext(context: NSManagedObjectContext = BDGCoreData.viewContext) {
if(context.hasChanges) {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
Now, I swear to god it never calls the method in this viewcontroller to create a new object: let activity = ActivityType(context: BDGCoreData.viewContext). I know how that works, and it truly is only called in a completely different view controller. I searched for it again in my entire project just in case, and it's really never called/created in any other places.
But somehow, in very rase cases, it saves the correct new order but also duplicates all objects in this list. Since it only happens rarely, I thought it might have something to do with threads? Which is why, as you can see in the code, I printed out the current thread, but at least when testing on my device, it seems to be on the main thread.
I'm truly stumped. I have a pretty good understanding of coredata and the app itself is quite complex with full of objects with different kind of relationships.
But why this happens? I have no clue... Does anyone have an idea?