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")
}
}
}
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.