为何SwiftData未显式插入却自动创建Line实体?
问题原因
SwiftData的@Relationship属性会自动追踪关联实体的状态。当你创建一个Line实例并将其关联到已被插入并持久化的Production实例时,SwiftData会自动将这个Line插入到当前的modelContext中——哪怕你只是把它存在本地@State数组里,没有显式调用modelContext.insert()。
在你的代码中,TextEnterScriptView接收的production是已经在ContentView中被插入到上下文并保存过的实体。每次在TextEditor输入时,你创建Line(content: "test line", production: production),将这个新Line的production属性指向已持久化的Production,SwiftData会通过关联关系自动将该Line加入上下文,导致ScriptView的@Query查询到新实体并显示。
另外,你的ScriptView中的@Query没有添加过滤条件,会查询所有Line实体,这也会导致所有自动插入的Line都被展示出来。
解决方案
1. 临时实体不关联持久化对象,确认后再保存
修改TextEnterScriptView,先创建不关联production的临时Line,等用户点击"Done"确认后,再关联并插入到上下文:
struct TextEnterScriptView: View { @Environment(\.dismiss) var dismiss @Environment(\.modelContext) var modelContext @State private var text = "" @State private var lines: [Line] = [] let production: Production var body: some View { NavigationStack { TextEditor(text: $text) .onChange(of: text, initial: false) { // 创建临时Line,不关联production lines = [Line(content: "test line", production: nil)] } .toolbar { Button("Done") { // 用户确认后,关联production并插入上下文 lines.forEach { line in line.production = production modelContext.insert(line) } // 保存上下文 do { try modelContext.save() } catch { print(error) } dismiss() } } } } }
2. 给ScriptView的@Query添加过滤条件
修改ScriptView,让@Query只查询当前production关联的Line,避免展示所有实体:
struct ScriptView: View { let production: Production @Query private var lines: [Line] @State private var isShowingSheet: Bool = false // 初始化Query时添加过滤条件 init(production: Production) { self.production = production _lines = Query(filter: #Predicate<Line> { $0.production == production }) } var body: some View { List { ForEach(lines) { line in Text(line.content) } } .toolbar { Button("Show Sheet") { isShowingSheet.toggle() } } .sheet(isPresented: $isShowingSheet) { TextEnterScriptView(production: production) } } }
额外说明
SwiftData的自动关联插入是设计特性,目的是简化关联实体的管理。如果需要临时创建实体用于编辑但不立即持久化,核心原则是不要将临时实体关联到已在上下文中的持久化实体,直到确认要保存为止。
内容的提问来源于stack exchange,提问作者The-Wolf

