I have the following code :
@Model
final class Parent {
@Relationship(deleteRule: .cascade)
var children: [Child]
init() {
children = []
}
}
@Model
final class Child {
@Relationship(deleteRule: .nullify)
var parent: Parent?
init() {
parent = nil
}
}
struct ContentView: View {
@Environment(\.modelContext) private var context
var body: some View {
VStack {
View1()
Divider()
View2()
Divider()
View3()
Divider()
View4()
}
}
func View1() -> some View {
// Works perfectly
let parentFetch = FetchDescriptor<Parent>()
let childFetch = FetchDescriptor<Child>()
return Text("1 - Parents: \(try! context.fetchCount(parentFetch)) - Children: \(try! context.fetchCount(childFetch))")
}
func View2() -> some View {
// Works perfectly
let parentFetch = FetchDescriptor<Parent>()
let childFetch = FetchDescriptor<Child>()
let parent = Parent()
context.insert(parent)
let child1 = Child()
child1.parent = parent
let child2 = Child()
child2.parent = parent
let child3 = Child()
child3.parent = parent
return VStack {
Text("2 - Parents: \(try! context.fetchCount(parentFetch)) - Children: \(try! context.fetchCount(childFetch))")
Text("2 - Parent has: \(parent.children.count) children")
}
}
func View3() -> some View {
// Works perfectly
let parentFetch = FetchDescriptor<Parent>()
let childFetch = FetchDescriptor<Child>()
let children = try! context.fetch(childFetch)
context.delete(children[0])
try! context.save()
let parent = (try! context.fetch(parentFetch))[0]
return VStack {
Text("3 - Parents: \(try! context.fetchCount(parentFetch)) - Children: \(try! context.fetchCount(childFetch))")
Text("3 - Parent has: \(parent.children.count) children")
}
}
func View4() -> some View {
// Just doesn't work
let parentFetch = FetchDescriptor<Parent>()
let childFetch = FetchDescriptor<Child>()
let parent = (try! context.fetch(parentFetch))[0]
context.delete(parent)
// Children won't be deleted !!!
try! context.save()
return VStack {
Text("4 - Parents: \(try! context.fetchCount(parentFetch)) - Children: \(try! context.fetchCount(childFetch))")
}
}
}
When deleting the parent in View4(), I expect the children to be deleted too, as a consequence of the .cascade rule. But that's not the case, as shown here:

I have tried with and without specifying the inverse, with or with optional...
What's the trick, is that a known bug ? I found no trace of it.
OK, since it appears to be a bug in Swiftdata (I wonder how they could miss this one, but it seems Swiftdata is not mature, as I also found also two other obvious bugs), I propose the following work-around:
extension ModelContext {
///
/// TODO: BE REMOVED WHEN `.cascade` is fixed
///
public func delete(parent: Parent) {
for child in parent.children {
self.delete(child)
}
self.delete(parent)
}
}
Not ideal, as not API transparent, and also, you need to know you're deleting a parent.
But I'll accept any better workaround.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With