I have the following code:
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
Button("add", action: {
let schoolClass = SchoolClass(name: "test")
modelContext.insert(schoolClass)
let schoolClassSubject = SchoolClassSubject(schoolClass: schoolClass)
schoolClass.schoolClassSubjects.append(schoolClassSubject)
})
}
.padding()
}
}
@Model
class SchoolClass {
var name: String
var schoolClassSubjects: [SchoolClassSubject] = []
init(name: String) {
self.name = name
}
}
@Model
class SchoolClassSubject {
var schoolClass: SchoolClass
init(schoolClass: SchoolClass) {
print("test")
self.schoolClass = schoolClass
}
}
#Preview {
ContentView()
}
The line let schoolClassSubject = SchoolClassSubject(schoolClass: schoolClass)
breaks with the following exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Illegal attempt to establish a relationship 'schoolClass' between objects in different contexts (source = <NSManagedObject: 0x60000214cc30> (entity: SchoolClassSubject; id: 0x60000024ebe0 ; data: { schoolClass = nil; }) , destination = <NSManagedObject: 0x60000214cd70> (entity: SchoolClass; id: 0x600000276740 ; data: { name = test; schoolClassSubjects = ( ); }))' *** First throw call
Does anyone understand why? The exception mentions different contexts but the schoolClassSubject is completely new.
Any help is appreciated.
This is actually two problems that needs to be resolved.
Firstly the property schoolClassSubjects
should be a relationship so change
var schoolClassSubjects: [SchoolClassSubject] = []
to
@Relationship(.nullify, inverse: schoolClass) var schoolClassSubjects: [SchoolClassSubject]
Above I used .nullify
as the delete rule but you need to pick one rule that fits your case.
and secondly things needs to be done in the right order.
Because you insert one object into the ModelContext first and then you create the other one and a relationship between them you have a situation where one is in a model context and the other one isn't, this is somewhat incorrectly reported as being in "different contexts".
One way to fix this is to create both objects and the relationships and then insert them into the context and everything will be persisted as expected. I tried some variations on doing this and there seems to be more than one way to make this work but here is what I settled for:
let schoolClass = SchoolClass(name: "test")
let schoolClassSubject = SchoolClassSubject(schoolClass: schoolClass)
modelContext.insert(schoolClass)
modelContext.insert(schoolClassSubject)
Note that we only have to set one end of the relationship now, SwiftData handles the inverse for us.