Inserting a Model entity with a relationship results in a runtime error.

Hi,

when inserting an entity with a relationship I get the following runtime error: Illegal attempt to establish a relationship 'group' between objects in different contexts [...].

The model looks like this:

@Model
class Person {
var name: String
@Relationship(.nullify, inverse: \Group.members) var group: Group
init(name: String) {
self.name = name
}
}
@Model
class Group {
var name: String
@Relationship(.cascade) public var members: [Person]
init(name: String) {
self.name = name
}
}

It can be reproduced using this (contrived) bit of code:

let group = Group(name: "Group A")
ctx.insert(group)
try! ctx.save()
let descriptor = FetchDescriptor<Group>()
let groups = try ctx.fetch(descriptor)
XCTAssertFalse(groups.isEmpty)
XCTAssertEqual(groups.count, 1)
XCTAssertTrue(groups.first?.name == "Group A")
let person = Person(name: "Willy")
person.group = group
ctx.insert(person)
try ctx.save()

(See also full test case below).

Anybody experiencing similar issues? Bug or feature?

Cheers, Michael


Full test case:

import SwiftData
import SwiftUI
import XCTest
// MARK: - Person -
@Model
class Person {
var name: String
@Relationship(.nullify, inverse: \Group.members) var group: Group
init(name: String) {
self.name = name
}
}
// MARK: - Group -
@Model
class Group {
var name: String
@Relationship(.cascade) public var members: [Person]
init(name: String) {
self.name = name
}
}
// MARK: - SD_PrototypingTests -
final class SD_PrototypingTests: XCTestCase {
var container: ModelContainer!
var ctx: ModelContext!
override func setUpWithError() throws {
let fullSchema = Schema([Person.self,
Group.self,])
let dbCfg = ModelConfiguration(schema: fullSchema)
container = try ModelContainer(for: fullSchema, dbCfg)
ctx = ModelContext(container)
_ = try ctx.delete(model: Group.self)
_ = try ctx.delete(model: Person.self)
}
override func tearDownWithError() throws {
guard let dbURL = container.configurations.first?.url else {
XCTFail("Could not find db URL")
return
}
do {
try FileManager.default.removeItem(at: dbURL)
} catch {
XCTFail("Could not delete db: \(error)")
}
}
func testRelAssignemnt_FB12363892() throws {
let group = Group(name: "Group A")
ctx.insert(group)
try! ctx.save()
let descriptor = FetchDescriptor<Group>()
let groups = try ctx.fetch(descriptor)
XCTAssertFalse(groups.isEmpty)
XCTAssertEqual(groups.count, 1)
XCTAssertTrue(groups.first?.name == "Group A")
let person = Person(name: "Willy")
person.group = group
ctx.insert(person)
try ctx.save()
}
}
@Relationship(.nullify, inverse: \Group.members) var group: Group

group must be optional

Does it make any difference if you change this code:

let person = Person(name: "Willy")
person.group = group
ctx.insert(person)

to this?

let person = Person(name: "Willy")
ctx.insert(person)
person.group = group

That is, to insert both objects into the context before attempting to establish their relationship?

@Purkylin_glow making var optional doesn't work on Xcode 15 Beta 7. That's my case that worked before:

@Model
final class Category {
...
@Relationship(deleteRule: .cascade) var incomes: [Income]
...
}
@Model
final class Income {
...
@Relationship(inverse: \Category.incomes) var category: Category?
...
}

I'm getting the same error. I am trying to do like a map thing, where a map, has many grid squares, which has many elevation points. If I delete a map, the map, all the grid squares and elevation points are deleted. If I delete a grid square, the grid square and all the elevation points are deleted. oddly it seems to be only the relationship between the last two classes that is the problem. hp_baseboard to be precise. I generally stick to microcontrollers for programming, so this is new.

final class Route {
//var timestamp: Date
@Attribute(.unique) var route_name: String
var route_season: String
var route_processed: Bool
var route_coordinates: String
@Relationship(deleteRule: .cascade, inverse: \Baseboard.baseboard_route) var route_baseboards: [Baseboard] = [] //[UUID]
init(route_name: String, route_season: String, route_processed: Bool, route_coordinates: String, route_baseboards: [Baseboard]) {
//self.timestamp = timestamp
self.route_name = route_name
self.route_season = route_season
self.route_processed = route_processed
self.route_coordinates = route_coordinates
self.route_baseboards = route_baseboards
}
}
@Model
final class Baseboard {
//var baseboard_location: (Int, Int) //will have to decide which is which row/col
var baseboard_column: Int
var baseboard_row: Int
var texture: [String]
var baseboard_processed: Bool
var baseboard_grid_size: Int
var baseboard_route: Route?
@Relationship(deleteRule: .cascade, inverse: \Height_Point.hp_baseboard) var baseboard_heightPoints: [Height_Point] = []
init(/*baseboard_location: (Int, Int)*/baseboard_column: Int, baseboard_row: Int, texture: [String], baseboard_processed: Bool, baseboard_grid_size: Int, baseboard_route: Route, baseboard_heightPoints: [Height_Point]) {
//self.baseboard_location = baseboard_location
self.baseboard_column = baseboard_column
self.baseboard_row = baseboard_column
self.texture = texture
self.baseboard_processed = baseboard_processed
self.baseboard_grid_size = baseboard_grid_size
self.baseboard_route = baseboard_route
self.baseboard_heightPoints = baseboard_heightPoints
}
}
@Model
final class Height_Point {
var hp_baseboard: Baseboard?
//var hp_location: (Int, Int)
var hp_column: Int
var hp_row: Int
var hp_processed: Bool
var hp_elevation: Float
var hp_texture: String
init(hp_baseboard: Baseboard, /*hp_location: (Int, Int),*/ hp_column: Int, hp_row: Int, hp_processed: Bool, hp_elevation: Float, hp_texture: String) {
self.hp_baseboard = hp_baseboard
//self.hp_location = hp_location
self.hp_column = hp_column
self.hp_row = hp_row
self.hp_processed = hp_processed
self.hp_elevation = hp_elevation
self.hp_texture = hp_texture
}
}

The problem is inserting the newItem3

withAnimation {
let newItem = Route(route_name: "test route " + UUID().uuidString, route_season: "summer", route_processed: false, route_coordinates: "Somewhere", route_baseboards: []
)
modelContext.insert(newItem)
let newItem2 = Baseboard(baseboard_column: 0, baseboard_row: 0, texture: ["Grid"], baseboard_processed: false, baseboard_grid_size: 10, baseboard_route: newItem, baseboard_heightPoints: []
)
/*modelContext.insert(newItem2)
newItem2.baseboard_route?.route_baseboards.append(newItem2)*/
let newItem3 = Height_Point(
hp_baseboard: newItem2,
hp_column: 0,
hp_row: 0,
hp_processed: false,
hp_elevation: 0,
hp_texture: "Grid"
)
modelContext.insert(newItem3)
/*newItem3.hp_baseboard? .baseboard_heightPoints.append(newItem3)*/
}
}

if I don't put something in for baseboard_Route or hp_Baseboard, then it throws up another problem. Yet I'm not sure this is correct either.

Inserting a Model entity with a relationship results in a runtime error.
 
 
Q