admin管理员组

文章数量:1202803

SwiftData delete isn't working, when I attempt to delete a model, my app crashes and I get the following error:

SwiftData/PersistentModel.swift:359: Fatal error: Cannot remove My_App.Model2 from relationship Relationship - name: model2, options: [], valueType: Model2, destination: Model2, inverseName: models3, inverseKeypath: Optional(\Model2.models3) on My_App.Model3 because an appropriate default value is not configured.

I get that it's saying I don't have a default value, but why do I need one? Isn't @Relationship .cascade automatically deleting the associated models?

And onto of that, why is the error occurring within the do block, shouldn't it be caught by the catch, and printed?

I have put together a sample project below.

import SwiftUI
import SwiftData

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(for: Model3.self)
        }
    }
}

@Model
class Model1 {
    var name: String
    @Relationship(deleteRule: .cascade, inverse: \Model2.model1) var models2: [Model2] = []
    init(name: String) {
        self.name = name
    }
}

@Model
class Model2 {
    var name: String
    var model1: Model1
    @Relationship(deleteRule: .cascade, inverse: \Model3.model2) var models3: [Model3] = []
    init(name: String, model1: Model1) {
        self.name = name
        self.model1 = model1
    }
}

@Model
class Model3 {
    var name: String
    var model2: Model2
    init(name: String, model2: Model2) {
        self.name = name
        self.model2 = model2
    }
}


struct ContentView: View {
    @Query var models1: [Model1]
    @Environment(\.modelContext) var modelContext
    var body: some View {
        NavigationStack {
            List(models1) { model1 in
                Text(model1.name)
                    .swipeActions {
                        Button("Delete", systemImage: "trash", role: .destructive) {
                            modelContext.delete(model1)
                            do {
                                try modelContext.save()
                                //SwiftData/PersistentModel.swift:359: Fatal error: Cannot remove My_App.Model2 from relationship Relationship - name: model2, options: [], valueType: Model2, destination: Model2, inverseName: models3, inverseKeypath: Optional(\Model2.models3) on My_App.Model3 because an appropriate default value is not configured.
                            } catch {
                                print(error.localizedDescription)
                            }
                        }
                    }
            }
            .toolbar {
                Button("Insert", systemImage: "plus") {
                    modelContext.insert(Model3(name: "model3", model2: Model2(name: "model2", model1: Model1(name: "model1"))))
                }
            }
        }
    }
}

SwiftData delete isn't working, when I attempt to delete a model, my app crashes and I get the following error:

SwiftData/PersistentModel.swift:359: Fatal error: Cannot remove My_App.Model2 from relationship Relationship - name: model2, options: [], valueType: Model2, destination: Model2, inverseName: models3, inverseKeypath: Optional(\Model2.models3) on My_App.Model3 because an appropriate default value is not configured.

I get that it's saying I don't have a default value, but why do I need one? Isn't @Relationship .cascade automatically deleting the associated models?

And onto of that, why is the error occurring within the do block, shouldn't it be caught by the catch, and printed?

I have put together a sample project below.

import SwiftUI
import SwiftData

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(for: Model3.self)
        }
    }
}

@Model
class Model1 {
    var name: String
    @Relationship(deleteRule: .cascade, inverse: \Model2.model1) var models2: [Model2] = []
    init(name: String) {
        self.name = name
    }
}

@Model
class Model2 {
    var name: String
    var model1: Model1
    @Relationship(deleteRule: .cascade, inverse: \Model3.model2) var models3: [Model3] = []
    init(name: String, model1: Model1) {
        self.name = name
        self.model1 = model1
    }
}

@Model
class Model3 {
    var name: String
    var model2: Model2
    init(name: String, model2: Model2) {
        self.name = name
        self.model2 = model2
    }
}


struct ContentView: View {
    @Query var models1: [Model1]
    @Environment(\.modelContext) var modelContext
    var body: some View {
        NavigationStack {
            List(models1) { model1 in
                Text(model1.name)
                    .swipeActions {
                        Button("Delete", systemImage: "trash", role: .destructive) {
                            modelContext.delete(model1)
                            do {
                                try modelContext.save()
                                //SwiftData/PersistentModel.swift:359: Fatal error: Cannot remove My_App.Model2 from relationship Relationship - name: model2, options: [], valueType: Model2, destination: Model2, inverseName: models3, inverseKeypath: Optional(\Model2.models3) on My_App.Model3 because an appropriate default value is not configured.
                            } catch {
                                print(error.localizedDescription)
                            }
                        }
                    }
            }
            .toolbar {
                Button("Insert", systemImage: "plus") {
                    modelContext.insert(Model3(name: "model3", model2: Model2(name: "model2", model1: Model1(name: "model1"))))
                }
            }
        }
    }
}
Share Improve this question edited Jan 29 at 8:43 DarkBee 15.6k8 gold badges70 silver badges114 bronze badges asked Jan 21 at 2:19 The-WolfThe-Wolf 436 bronze badges 5
  • 1 In Model2 you could try var model1: Model1?, similarly in Model3, var model2: Model2?. Works for me. – workingdog support Ukraine Commented Jan 21 at 2:36
  • That works @workingdogsupportUkraine, only downside is that I have to unwrap the value every time I use it. Is the error a problem with the way I'm structuring my schema, or does SwiftData need all references to be optional? – The-Wolf Commented Jan 21 at 4:19
  • 1 Not 100% sure, but I think SwiftData needs to know what to do when you delete a model that contains other models through relationships, eg set it to nil, which means having a Optional. – workingdog support Ukraine Commented Jan 21 at 5:17
  • 1 You need to set the relationship from both sides. One to many and many to one. For the belongs to relationship you need to make sure that it is optional. Like the relationship from Model2 back to Model1 should be Model1? This will also be requirement if you ever want to sync with iCloud. – azamsharp Commented Jan 21 at 5:25
  • To me it looks like a bug where SwiftData can’t handle chained relationships (and delete rules) correctly. I think you need to manually delete all objects in models2 before deleting the Model1 object. – Joakim Danielson Commented Jan 21 at 7:04
Add a comment  | 

1 Answer 1

Reset to default 0

The solution as suggested by @workingdogsupportUkraine and confirmed by Apple is to make the relationships optional.

Like this:

@Model
class Model2 {
    var name: String
    var model1: Model1?
    ...
}

@Model
class Model3 {
    var name: String
    var model2: Model2?
    ...
}

本文标签: