SwiftData: "Illegal attempt to establish a relationship 'item' between objects in different contexts

I have run into this SwiftData issue in multiple projects and have been able to replicate it by building off of the default SwiftData launch project.

The original Item class:

class Item {
    var timestamp: Date
    
    init(timestamp: Date) {
        self.timestamp = timestamp
    }
}

New MyItem class to replicate the error. Notice I nest an Item object inside MyItem:

class MyItem {
    var name: String
    var item: Item
    
    init(name: String, item: Item) {
        self.name = name
        self.item = item
    }
}

I then build off of the default view for a SwiftData project. When the '+' button is pressed, a new list item for both Item and MyItem should appear in their appropriate sections.

    @Environment(\.modelContext) private var modelContext
    @Query private var items: [Item]
    @Query private var myItems: [MyItem]

    var body: some View {
        NavigationSplitView {
            List {
                Section("All Items") {
                    ForEach(items) { item in
                        NavigationLink {
                            Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
                        } label: {
                            Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
                        }
                    }
                }
                
                Section("My Items") {
                    ForEach(myItems) { myItem in
                        NavigationLink {
                            Text("Item at \(myItem.item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
                        } label: {
                            HStack {
                                Text(myItem.name)
                                Spacer()
                                Text(myItem.item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
                            }
                        }
                    }
                }
            }
            .toolbar {
                ToolbarItem {
                    Button(action: addItem) {
                        Label("Add Item", systemImage: "plus")
                    }
                }
            }
        } detail: {
            Text("Select an item")
        }
    }

    private func addItem() {
        withAnimation {
            let newItem = Item(timestamp: Date())
            modelContext.insert(newItem)
            
            let newMyItem = MyItem(name: "Test", item: newItem)
            modelContext.insert(newMyItem)
        }
    }
}

The app crashes and I get the following error when I attempt to click the '+' button (which should create a new Item and MyItem in the modelContext:

Thread 1: "Illegal attempt to establish a relationship 'item' between objects in different contexts (source = <NSManagedObject: 0x600002166940> (entity: MyItem; id: 0x600000298240 <x-coredata:///MyItem/t2D4951EB-0D2F-44B1-AF8C-5A1BB11659F53>; data: {\n item = nil;\n name = Test;\n}) , destination = <NSManagedObject: 0x600002174000> (entity: Item; id: 0x600000232440 <x-coredata:///Item/t2D4951EB-0D2F-44B1-AF8C-5A1BB11659F52>; data: {\n timestamp = "2023-10-04 18:21:21 +0000";\n}))"

Can anyone help me understand the new SwiftData framework in this regard? I am still new to SwiftUI.

Post not yet marked as solved Up vote post of jklawlor Down vote post of jklawlor
1.5k views

Replies

Forgot to mention, I made sure to add the new MyItem type to the schema as well:

struct SwiftDataBugTestApp: App {
    var sharedModelContainer: ModelContainer = {
        let schema = Schema([
            Item.self, MyItem.self
        ])
        let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)

        do {
            return try ModelContainer(for: schema, configurations: [modelConfiguration])
        } catch {
            fatalError("Could not create ModelContainer: \(error)")
        }
    }()

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(sharedModelContainer)
    }
}

Any luck figuring this one out? I have two almost identical situations in my app ... one works, the other doesn't ... and I can't seem to figure out why. They both get the ModelContext from the same environment so the error message doesn't make much sense.

What I find interesting is that the crash (for me) occurs not upon calling insert using the ModelContext, but in the constructor of the @Model (e.g. MyItem in your example). This is another surprise as I don't this the ModelContext is even used by the backingData.

You have to add Item to MyItem after Init:

class MyItem {
    var name: String
    var item: Item?
    
    init(name: String, item: Item) {
        self.name = name
        setItem(item)
    }

    func setItem(_ item: item) {
        self.item = item
    }

}

  • Thank you for sharing this. It worked for me as well. No idea why but at least I can move forward. I assume its a bug in SwiftData.

  • Wow, been struggling with this issue, and find your solution that worked.. Thanks for sharing, but also wanted to ask if there is any explanation why this workaround works and the usual approach as setting from init not?

Add a Comment

Still no solution that I’ve found. I posted on the Twitter dev community as well, and no one can find a solution. With further testing, I have found that sometimes it’s the type that you’re trying to use with the @Model macro. For example, it will throw a similar thread error if you attempt to use an enum type in a model as well.

You cannot set item directly in the init(name:item:) for MyItem class.

This should work

private func addItem() {
    withAnimation {
        let newItem = Item(timestamp: Date())
        modelContext.insert(newItem)
            
        let newMyItem = MyItem(name: "Test")
        modelContext.insert(newMyItem)
        newMyItem.item = newItem
    }
}

After lots of trial and error, I got this working when I run the app for the first time. But when I force quit, the app does not load subsequently. Similar to what all of you are reporting, it works with some of my entities, but not with others, without any easily discernible difference between them.

It's because all SwiftData relationships must be optional, at least at this time and in my experience thus far. I've received this error quite a bit.

Changing your code to the below should resolve your issue, I'd be curious if not

class MyItem {
    var name: String
    var item: Item?
    
    init(name: String, item: Item? = nil) {
        self.name = name
        self.item = item
    }
}

I just resolved this issue for myself. Per @milutz in this response: https://developer.apple.com/forums/thread/733519. The relationship can't be set in the init which requires the relationship to be optional. To get your code to work, you'd need to update addItem() to set the relationship after you insert the new object.

I can confirm that the issue exists for iOS17.0.1 and macOS14.0. However it is fixed for iOS17.1.2. (I didn't update my mac to test.) So if we are targeting iOS17, we must make our relationship instances optional and set the values after the instance is initialized as Diethus showed in code.

Additionally, we can combine implicit unwrapping, so the callers won't notice it's an optional.

class MyItem {
    var name: String
    private(set) var item: Item!
    
    init(name: String, item: Item) {
        self.name = name
        
        // relationship attributes must be set after the instance is initialized.
        self.item = item
    }
}