3

I have the following code:

import SwiftUI
import SwiftData

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext

    var schoolClass: SchoolClass

    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
            Button("add", action: {
                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
    }
}

schoolClass is already saved in swiftData and passed as a property to 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 x-coredata:///SchoolClassSubject/t186670BB-357C-400C-8D6F-178BEB8F4B473; data: { schoolClass = nil; }) , destination = <NSManagedObject: 0x60000214cd70> (entity: SchoolClass; id: 0x600000276740 x-coredata:///SchoolClass/t186670BB-357C-400C-8D6F-178BEB8F4B472; data: { name = test; schoolClassSubjects = ( ); }))' *** First throw call

If I change my Model to this:

@Model
class SchoolClassSubject {
    var schoolClass: SchoolClass? = nil
    init() {
    }
}

And my saving code to this:

let schoolClassSubject = SchoolClassSubject()
modelContext.insert(schoolClassSubject)
schoolClassSubject.schoolClass = schoolClass
schoolClass.schoolClassSubjects.append(schoolClassSubject)

But I don't want to make schoolClass in schoolClassSubject optional as it is not in reality. How can I make it not optional and still save it without the given error?

HangarRash
  • 7,314
  • 5
  • 5
  • 32
David Schilling
  • 2,442
  • 3
  • 17
  • 24

1 Answers1

2

This answer was written using Xcode 15.0 beta 2, in future versions I assume this answer will become irrelevant and we can handle this more easily

Once again, you must use the @Relationship annotation for the relationships to work properly and once you have that SwiftData will handle them for you.

And because of that you should not include any relationship properties in any init methods for your model.

So change the init in SchoolClassSubject to

init() {}

(I don't know if it is a bug that we need an empty init here or if this is just an odd case because there are no other properties)

And then change the code in the button action to

let schoolClassSubject = SchoolClassSubject()
modelContext.insert(schoolClassSubject)
schoolClassSubject.schoolClass = schoolClass

To clarify the above code:

  • First create an instance of your model object
  • Then insert the object into the model context
  • Lastly assign any relationship properties to your object
  • (save)
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
  • This only works for me if `schoolClass` in `schoolClassSubject` is optional. But i want it to be not optional as stated in the question. – David Schilling Jul 04 '23 at 05:13
  • I was using the exact same models as you have, nothing is optional. – Joakim Danielson Jul 04 '23 at 05:28
  • I actually ran into the exact same problem with my own code today and the solution was exactly as stated in this answer. – Joakim Danielson Jul 04 '23 at 13:43
  • @JoakimDanielson – How are you solving it on the latest beta (7)? As this doesn't seem to be working anymore. – Thecafremo Aug 28 '23 at 22:37
  • @Thecafremo Yes, looks like [this question](https://stackoverflow.com/questions/76992387/swiftdata-inverse-relationship-stop-working-in-xcode-15-beta-7). I haven’t had time to look into it more than what is mentioned in the comments – Joakim Danielson Aug 29 '23 at 05:57
  • @JoakimDanielson – I was hoping there would be a better solution. Thanks, though! – Thecafremo Aug 29 '23 at 10:53