SwiftData "Auto Inserts" array into ModelContext

Definitely one of the stranger quirks of SwiftData I've come across.

I have a ScriptView that shows Line entities related to a Production, and a TextEnterScriptView that’s presented in a sheet to input text.

I’m noticing that every time I type in the TextEditor within TextEnterScriptView, a new Line shows up in ScriptView — even though I haven’t explicitly inserted it into the modelContext.

I'm quite confused because even though I’m only assigning a new Line to a local @State array in TextEnterScriptView, every keystroke in the TextEditor causes a duplicate Line to appear in ScriptView.

In other words, Why is SwiftData creating new Line entities every time I type in the TextEditor, even though I’m only assigning to a local @State array and not explicitly inserting them into the modelContext?

Here is my minimal reproducible example:

import SwiftData
import SwiftUI

@main
struct testApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(for: Line.self, isAutosaveEnabled: false)
        }
    }
}

struct ContentView: View {
    @Environment(\.modelContext) var modelContext
    @Query(sort: \Production.title) var productions: [Production]
    var body: some View {
        NavigationStack {
            List(productions) { production in
                NavigationLink(value: production) {
                    Text(production.title)
                }
            }
            .navigationDestination(for: Production.self) { production in
                ScriptView(production: production)
            }
            .toolbar {
                Button("Add", systemImage: "plus") {
                    let production = Production(title: "Test \(productions.count + 1)")
                    modelContext.insert(production)
                    do {
                        try modelContext.save()
                    } catch {
                        print(error)
                    }
                }
            }
            .navigationTitle("Productions")
        }
    }
}

struct ScriptView: View {
    @Query private var lines: [Line]
    let production: Production
    @State private var isShowingSheet: Bool = false
    var body: some View {
        List {
            ForEach(lines) { line in
                Text(line.content)
            }
        }
        .toolbar {
            Button("Show Sheet") {
                isShowingSheet.toggle()
            }
        }
        .sheet(isPresented: $isShowingSheet) {
            TextEnterScriptView(production: production)
        }
    }
}

struct TextEnterScriptView: View {
    @Environment(\.dismiss) var dismiss
    @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) {
                    lines = [Line(content: "test line", production: production)]
                }
                .toolbar {
                    Button("Done") {
                        dismiss()
                    }
                }
        }
    }
}

@Model
class Production {
    @Attribute(.unique) var title: String

    @Relationship(deleteRule: .cascade, inverse: \Line.production)
    var lines: [Line] = []

    init(title: String) {
        self.title = title
    }
}

@Model
class Line {
    var content: String
    var production: Production?

    init(content: String, production: Production?) {
        self.content = content
        self.production = production
    }
}

SwiftData does it automatically for you and it must do it or the autosave functionality wouldn't work.

If you would only save one side of a relationship then when the app is restarted the other side would be nil or it crashes if the property is non-optional.

SwiftData "Auto Inserts" array into ModelContext
 
 
Q