在SwiftData中删除对象时,可能无法触发级联删除。
问题描述
下面是实际案例的复现:
1、PiggyBank存钱罐对象
@Relationship(deleteRule: .cascade, inverse: \SavingsRecord.piggyBank)
var records: [SavingsRecord]? = []
2、SavingsRecord存取记录对象
@Relationship(inverse: \PiggyBank.records)
var piggyBank: PiggyBank?
两者的关系是一对多的关系,一个PiggyBank对应多个SavingsRecord对象。
3、删除PiggyBank存钱罐
context.delete(data)
try? context.save()
删除PiggyBank存钱罐时,发现关联的SavingsRecord存取记录并没有同步删除,实际上是没有触发级联删除,因此存取记录被保留了下来。

排查过程
后来迭代新版本,发现级联删除的问题得到了解决。使用 GitHub 获取历史版本对比,发现当迭代新版本修改的代码如下:
context.delete(data)
let fetchRequest = FetchDescriptor<PiggyBank>(
...
existingPiggyBanks = try context.fetch(fetchRequest)
...
)
try? context.save()
推断是context.fetch(fetchRequest)触发上下文双薪,导致SwiftData刷新上下文状态,处理待删除对象的关系,并触发关系图的重新计算。
下面通过测试进一步验证了这一结论:
context.delete(data)
let _ = try? context.fetch(FetchDescriptor<PiggyBank>()) // 触发上下文刷新
try? context.save()
添加context.fetch代码后,没有触发级联删除的问题得到解决。删除PiggyBank对象时,会同步删除关联的存取记录。
总结
SwiftData在正确配置 @Relationship 关系后,如果在删除SwiftData时,没有触发级联删除,需要使用context.fetch重新触发上下文刷新。
这个问题可能是SwiftData的BUG。
除了使用fetch()触发以外,还可以手动级联删除:
if let records = data.records {
for record in records {
context.delete(record)
}
}
context.delete(data)
try context.save()
