CoreData Failure During Entity Creation in the ViewContext

I have a program that reads in 3 json files and updates CoreData if one or more of the JSON files contains new data. It runs successfully 9 times out of 10. The failure occurs during the creation of an entity in the managed object context. The failure occurs in the function AddRecordsToCoreData. I have placed the error messages below the offending line of code. Further, it appears to only fail during the third call to UpdateCoreDataRecoreds which in turn calls AddRecordsToCoreData. Setting up the calls to run in series I thought I could eliminate any problems but clearly not the case. What error am I making in the code? Or is there some other approach I should utilize? Below is the code in question.

class UpdateCoreData: ObservableObject {
    
    let persistentContainer = CoreDataStack.shared.persistentContainer
    @Published var loadingData: Bool = true
    @Published var updateStatus: String = ""
    
    init() {
        Task {
            async let fund1Complete: Bool = UpdateCoreDataRecords(fundName: "Fund1", moc: persistentContainer.viewContext)
            let _ = await (fund1Complete)
            async let fund2Complete: Bool = UpdateCoreDataRecords(fundName: "Fund2", moc: persistentContainer.viewContext)
            let _ = await (fund2Complete)
            async let fund3Complete: Bool = UpdateCoreDataRecords(fundName: "Fund3", moc: persistentContainer.viewContext)
            let _ = await (fund3Complete)
            
            persistentContainer.viewContext.vacuum()
            let persistentStore = persistentContainer.persistentStoreCoordinator.persistentStores.first
            do {
                try persistentContainer.persistentStoreCoordinator.remove(persistentStore!)
            } catch {
                print("Unable to remove store -> \(error)")
            }
            DispatchQueue.main.async {
                self.loadingData = false
                self.updateStatus = "Core Date is Updated"
                
            }
        }
    }
}

func UpdateCoreDataRecords(fundName: String, moc: NSManagedObjectContext) async -> Bool {
    
    var latestDate: Date = Date()
    var decodedJSON: TradingDays = TradingDays.init(tradingday: [])
    latestDate = await LatestCoreDataDate(fundName: fundName, moc: moc)
    decodedJSON = await DecodeJSONFile(fileName: fundName)
    
    await AddRecordsToCoreData(jsonData: decodedJSON, fundName: fundName, latestDate: latestDate, moc: moc)
    return true
}

func LatestCoreDataDate(fundName: String, moc: NSManagedObjectContext) async -> Date {
    
    var coreDataValues: [TradingDayClose] = []
    var latestDate: Date = Date()
    
    let fetchRequest: NSFetchRequest<TradingDayClose> = TradingDayClose.fetchRequest()
    fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timeStamp", ascending: false)]
    fetchRequest.predicate = NSPredicate(format: "fundName = %@", fundName)
    fetchRequest.fetchLimit = 1
    
    do {
        coreDataValues = try moc.fetch(fetchRequest)
    } catch let error {
        print("Error fetching max date. \(error.localizedDescription)")
    }
    
    if coreDataValues.isEmpty {
        latestDate = Calendar.current.date(byAdding: DateComponents(year: -6), to: latestDate)!
    } else {
        latestDate = coreDataValues[0].timeStamp!
    }
    return latestDate
}

func AddRecordsToCoreData(jsonData: TradingDays, fundName: String, latestDate: Date, moc: NSManagedObjectContext) async {
    
    print("\(fundName)")
    for item in jsonData.tradingday {
        if item.timeStamp > latestDate {
            let newRecord = TradingDayClose(context: moc)
            // Thread 4: EXC_BAD_ACCESS (code=1, address=0x8)
            // thread 3 signal Sigbrt
            // Thread 2: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
            newRecord.fundName = fundName
            newRecord.id = UUID()
            newRecord.timeStamp = item.timeStamp
            newRecord.close = (item.close) as NSDecimalNumber
        } else {
            break
        }
    }
    if moc.hasChanges {
        do {
            print("Saving moc")
            try moc.save()
        } catch {
            print("Errors attempting to save moc")
        }
    }
}
Answered by deeje in 759632022

the persistentContainer.viewContext can only get and set records in the main thread, but if I'm reading your code correctly, you're dropping into a task (background thread). better to use persistentContainer.performBackgroundTask { backgroundContext in … } and pass the background context to all your functions.

Accepted Answer

the persistentContainer.viewContext can only get and set records in the main thread, but if I'm reading your code correctly, you're dropping into a task (background thread). better to use persistentContainer.performBackgroundTask { backgroundContext in … } and pass the background context to all your functions.

I updated my code but used persistentContainer.newBackgroundContext(). Also got rid of the Task and async. The code runs without failure and is very fast. Thank you again for the assistance. Oh, and I learned that I do not have to save twice.

class UpdateCoreData: ObservableObject {
    
    let persistentContainer = CoreDataStack.shared.persistentContainer
    @Published var loadingData: Bool = true
    @Published var updateStatus: String = ""
    
    init() {
        
        let bgContext = persistentContainer.newBackgroundContext()
        bgContext.performAndWait {
            do {
                var latestDate: Date = Date()
                var decodedJSON: TradingDays = TradingDays.init(tradingday: [])
                latestDate = LatestCoreDataDate(fundName: "Fund1", moc: bgContext)
                decodedJSON = DecodeJSONFile(fileName: "Fund1")
                AddRecordsToCoreData(jsonData: decodedJSON, fundName: "Fund1", latestDate: latestDate, moc: bgContext)
                
                latestDate = Date()
                decodedJSON = TradingDays.init(tradingday: [])
                latestDate = LatestCoreDataDate(fundName: "Fund2", moc: bgContext)
                decodedJSON = DecodeJSONFile(fileName: "Fund2")
                AddRecordsToCoreData(jsonData: decodedJSON, fundName: "Fund2", latestDate: latestDate, moc: bgContext)

                latestDate = Date()
                decodedJSON = TradingDays.init(tradingday: [])
                latestDate = LatestCoreDataDate(fundName: "Fund3", moc: bgContext)
                decodedJSON = DecodeJSONFile(fileName: "Fund3")
                AddRecordsToCoreData(jsonData: decodedJSON, fundName: "Fund3", latestDate: latestDate, moc: bgContext)
                
                try bgContext.save()
                
            } catch let error {
                print("error in background context = \(error)")
            }
        } // end perform and wait
        
        persistentContainer.viewContext.vacuum()
        let persistentStore = persistentContainer.persistentStoreCoordinator.persistentStores.first
        do {
            try persistentContainer.persistentStoreCoordinator.remove(persistentStore!)
        } catch {
            print("Unable to remove store -> \(error)")
        }
        
        DispatchQueue.main.async {
            self.loadingData = false
            self.updateStatus = "Core Date is Updated"
        }
    }
}

func LatestCoreDataDate(fundName: String, moc: NSManagedObjectContext) -> Date {

    var coreDataValues: [TradingDayClose] = []
    var latestDate: Date = Date()

    let fetchRequest: NSFetchRequest<TradingDayClose> = TradingDayClose.fetchRequest()
    fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timeStamp", ascending: false)]
    fetchRequest.predicate = NSPredicate(format: "fundName = %@", fundName)
    fetchRequest.fetchLimit = 1

    do {
        coreDataValues = try moc.fetch(fetchRequest)
    } catch let error {
        print("Error fetching max date. \(error.localizedDescription)")
    }

    if coreDataValues.isEmpty {
        latestDate = Calendar.current.date(byAdding: DateComponents(year: -6), to: latestDate)!
    } else {
        latestDate = coreDataValues[0].timeStamp!
    }
    return latestDate
}

func AddRecordsToCoreData(jsonData: TradingDays, fundName: String, latestDate: Date, moc: NSManagedObjectContext) {

    print("\(fundName)")
    for item in jsonData.tradingday {
        if item.timeStamp > latestDate {
            let newRecord = TradingDayClose(context: moc)
            newRecord.fundName = fundName
            newRecord.id = UUID()
            newRecord.timeStamp = item.timeStamp
            newRecord.close = (item.close) as NSDecimalNumber
        } else {
            break
        }
    }
}
CoreData Failure During Entity Creation in the ViewContext
 
 
Q