I'm working with SwiftData for the first time and having trouble with crashes when I try to set the value of a relationship.
I have a Profession
object which represents a job in the game. Each Profession
can have expenses (Expense
, like a mortgage payment).
I'm trying to populate the initial data when the app first launches. When I'm creating the first Profession
, I create one Expense
which seems to work but when I create a second Expense
the app crashes with Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1c3c6203c)
when trying to set the Expense.profession
relationship. Xcode expands the profession
property to show the get/set and it's crashing in the set function.
Am I missing something obvious? Or is SwiftData expecting me to do something very different than CoreData would in this case?
@Model
class Profession {
var id: UUID
var name: String
@Relationship(deleteRule: .cascade, inverse: \Expense.profession) var expenses: [Expense] = []
init(name: String) {
self.id = UUID()
self.name = name
}
convenience init(professiondefault: ProfessionDefault) {
self.init(name: professiondefault.name)
let taxes = Expense(name: "Taxes", cashflow: professiondefault.taxes, isPermanent: true, profession: self)
// crashes on the next line
let otherExpenses = Expense(name: "Other Expenses", cashflow: professiondefault.otherExpenses, isPermanent: true, profession: self)
}
}
@Model
class Expense {
let id: UUID
var cashflow: Int
let isPermanent: Bool
var name: String
var profession: Profession?
init(name: String, cashflow: Int, isPermanent: Bool, profession: Profession? = nil) {
self.id = UUID()
self.cashflow = cashflow
self.isPermanent = isPermanent
self.name = name
self.profession = profession
}
}
I believe I have found a pattern for how to make this work. When creating two related objects at the same time then you need insert the one you want to assign to into your model context first before connecting the two
So this works
let profession = Profession(name: "New profession")
modelContext.insert(profession) // <-- Insert first
let expense1 = Expense(name: "Expense 1", cashflow: 10, isPermanent: false)
let expense2 = Expense(name: "Expense 2", cashflow: 10, isPermanent: false)
profession.expenses.append(contentsOf: [expense1, expense2])
// this is also ok
// profession.expenses.append(expense1)
// profession.expenses.append(expense2)
And if we want to assign a profession to an expense this works:
let profession2 = Profession(name: "New profession")
let expense3 = Expense(name: "Expense 3", cashflow: 10, isPermanent: false)
let expense4 = Expense(name: "Expense 4", cashflow: 10, isPermanent: false)
modelContext.insert(expense3) // <-- Insert first
expense3.profession = profession2
modelContext.insert(expense4) // <-- Insert first
expense4.profession = profession2