No "upsert" when working with .unique attributes

Hi,

in the session the following is mentioned:

If a trip already exists with that name, then the persistent back end will update to the latest values. This is called an upsert. An upsert starts as an insert. If the insert collides with existing data, it becomes an update and updates the properties of the existing data.

Nevertheless, if I have a unique constraint on an (String) attribute and try to insert the same again, I end up in the debugger in the generated getter of the attribute:

@Attribute(.unique) public var name: String
{
    get {
        _$observationRegistrar.access(self, keyPath: \.name)
        return self.getValue(for: \.name) // <- here
    }

EXC_BREAKPOINT (code=1, subcode=0x1a8d6b724)

Am I missing something? If this is expected behaviour, how should I prevent this crash (other than checking for uniqueness before every insert)?

Thank you!

Cheers, Michael

Post not yet marked as solved Up vote post of milutz Down vote post of milutz
1.7k views

Replies

I've also come across this issue too

Hi, i've got the same issue.

Same issue, I have something like

var body: some View { List(cars) { car in NavigationLink(car.name, destination: CarDetailView(car: car)) } .onAppear(perform: { let newCar = Car(name: "Fordyota", date: [Date(date: Date())]) modelContext.insert(newCar) }) }

where name is @Attribute(.unique)

I also did not know what to do, here is my test model:

@Model
final class Item {
    @Attribute(.unique) var name: String
    
    init(name: String) {
        self.name = name
    }
}

Here is my test code:

func doTest() {
    let item1 = Item(name: "John")
    let item2 = Item(name: "John")
    let item3 = Item(name: "Peter")
    modelContext.insert(item1)
    try? modelContext.save()

    print("hello \(item1.name)") // <-- exception
}

And we have no methods to check what happened to non-unique items. And then I had an idea which worked for me, it is very simple, so instead of String type you should use String? (I know, this is inconvenient), but then if the item was non-unique the .name property will be set to nil!

So change you model to this

@Model
final class Item {
    @Attribute(.unique) var name: String? // <--- here (optional String)
    
    init(name: String) {
        self.name = name
    }
}

And now

func doTest() {
    let item1 = Item(name: "John")
    let item2 = Item(name: "John")
    let item3 = Item(name: "Peter")
    modelContext.insert(item1)
    try? modelContext.save()

// yeah, item1 was non-unique so it is now having a nil name
    if item1.name != nil {
        print("hello \(item1.name!)")
    }
}

Hope this, helps!