Swift crashing with signal SIGABRT with the core data

Viewed 53

In some ways, this code worked perfectly because it toggled on and off. Sometimes it works fine, sometimes it crashes, and sometimes it works perfectly. Could you please tell me what went wrong with this code?

func downloadExchangeRateFromPast(toDate: Date, fromCurrency: String) {
    startupCheck_ExchangeRateFromPast.enter()
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd"
    let fromDate = dateFormatter.string(from: toDate)
    
    let url = "https://api.exchangerate.host/convert?from=\(currentCurrency)&to=\(fromCurrency)&date=\(fromDate)"
    let session = URLSession(configuration: .default)
    
    session.dataTask(with: URL(string: url)!) { (data, response, _) in
        guard let JSONData = data else {
            startupCheck_ExchangeRateFromPast.leave()
            return
        }
        
        if let response = response as? HTTPURLResponse { print("Exchange Status: \(response.statusCode)") }
        
        do {
            let finalData = try JSONDecoder().decode(ExchangeRatePastJSON.self, from: JSONData)
            
            let newExchangeRate = ExchangeRateConvert(context: contextDataCore)
            newExchangeRate.date = toDate
            newExchangeRate.fromCode = fromCurrency
            newExchangeRate.toCode = currentCurrency
            newExchangeRate.rateConvert = finalData.result
            
            let newExchangeHistoryRecord = ExchangeHistoryRecord(context: contextDataCore) // Thread 20: signal SIGABRT
            newExchangeHistoryRecord.finalRecord = "\(toDate) - \(fromCurrency) - \(currentCurrency)"
            
            try contextDataCore.save()
            startupCheck_ExchangeRateFromPast.leave()
        } catch { startupCheck_ExchangeRateFromPast.leave() }
    }
    .resume()
}
1 Answers

The problem is that you might be using the NSManagedObjectContext on a background thread that is not associated with it.

If you add this argument to your scheme, the app will crash every time you access a context outside its queue:

-com.apple.CoreData.ConcurrencyDebug 1

To fix this issue, you can use the performBackgroundTask(_:) function and wrap your core data operations in a closure, which gives you an NSManagedObjectContext and executes the code in a private queue associated with it.

For example:

....
        persistentContainer.performBackgroundTask { backgroundContext in

            let newExchangeRate = ExchangeRateConvert(context: backgroundContext)
            newExchangeRate.date = toDate
            newExchangeRate.fromCode = fromCurrency
            newExchangeRate.toCode = currentCurrency
            newExchangeRate.rateConvert = finalData.result
            
            let newExchangeHistoryRecord = ExchangeHistoryRecord(context: backgroundContext)
            newExchangeHistoryRecord.finalRecord = "\(toDate) - \(fromCurrency) - \(currentCurrency)"
            
            try backgroundContext.save()
        }
....

Another solution, in case you don't have the persistentContainer handy, is to use the context .perform(_:) function which executes a block in the queue associated with the context:

....
        contextDataCore.perform {

            let newExchangeRate = ExchangeRateConvert(context: contextDataCore)
            newExchangeRate.date = toDate
            newExchangeRate.fromCode = fromCurrency
            newExchangeRate.toCode = currentCurrency
            newExchangeRate.rateConvert = finalData.result
            
            let newExchangeHistoryRecord = ExchangeHistoryRecord(context: contextDataCore) // Thread 20: signal SIGABRT
            newExchangeHistoryRecord.finalRecord = "\(toDate) - \(fromCurrency) - \(currentCurrency)"
            
            try contextDataCore.save()
        }
....

I don't recommend this approach because if the context was created with the main queue, it might block the UI momentarily and affect the user experience.

Related