I have an app that has one to many relationships and I am trying to load transactions into the app from a text file. As I load the transactions, I randomly hit on EXC_BAD_ACCESS at different times but always on a specific line. The weird part is that it does not crash every time I run the app, it crashes on different runs and on a different transaction but always on the line where I set the category (see below).
I am loading the transactions asynchronously using async/await. I have a progress bar that is updated using the MainActor thread. I am also using an actor to insert the transactions.
I tried to debug with Address Sanitizer, Thread Sanitizer and Zombie objects but did not find anything.
May be I am setting the relationships wrong. I thought it could be a data race issue but did not seem like it - not sure. May be a memory issue and was hoping zombie objects flag would be able to identify but it did not identify anything.
Any direction or help is really appreciated.
@Model
class Transaction: Hashable {
@Attribute(.unique) let id = UUID().uuidString
var date = Date()
... different properties
// @Relationship(inverse: \Category.transactions)
var category: Category?
init(date: Date = Date(), balance: Double = 0.0, category: Category) {
self.date = date
self.balance = balance
self.category = category //<-- always crashes here. I checked that category is a valid address
category.add(transaction: self)
}
}
@Model
class Category {
@Attribute(.unique) let id = UUID().uuidString
var name: String?
//... set properties
@Relationship(deleteRule: .cascade, inverse: \Transaction.category)
var transactions: [Transaction]? = []
init(name: String? = nil) {
self.name = name
}
func add(transaction: Transaction) {
if var transactions = self.transactions {
transactions.append(transaction)
}
}
}
Reading the text file with transactions
private func readFile(data: String) async {
... parse lines
// here is where the transaction is created and potentially the issue
let t = Transaction(date: date, category: category) //<--- trying to set the category in the Transaction.init
transactions.append(t)
}
// batching all transactions and inserting category after reading all data
await actor.insert(category) // insert now
Here is the problem it points to
{
// @Relationship(inverse: \Category.transactions)
@storageRestrictions(accesses: _$backingData, initializes: _category)
init(initialValue) {
_$backingData.setValue(forKey: \.category, to: initialValue)
_asset = _SwiftDataNoType()
}
// @Relationship(inverse: \Category.transactions)
get {
_$observationRegistrar.access(self, keyPath: \.category)
return self.getValue(forKey: \.category)
}
// @Relationship(inverse: \Category.transactions)
set {
_$observationRegistrar.withMutation(of: self, keyPath: \.category) {
self.setValue(forKey: \.category, to: newValue) <---- This is SwiftData code where it is trying to set to newValue triggering the crash
}
}
}
Here is where it crashes
@main
struct DataImportWithProgressViewApp: App { <---- Here is where it crashes with Thread1: EXC_BAD_ACCESS (code=1, address=0x8000000000000010)
let container = try! ModelContainer(
for: Category.self
)
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}