Submission Date
2026-08-16
Status
Open
Area
SwiftData
Operating System Version
macOS 26b5
Type
Incorrect/Unexpected Behavior
Description
Setting a property on a @Model instance to its existing value (e.g., item.title = item.title) marks both item.hasChanges and modelContext.hasChanges as true.
I expected SwiftData's hasChanges check to function like Core Data’s hasPersistentChangedValues, which evaluates whether the underlying persistent storage value has actually changed before marking an object or context as dirty.
Real-World Impact & Use Case
This behavior breaks standard SwiftUI form patterns when trying to enable or disable a "Save" button based on whether a model or context has pending edits.
When a SwiftUI TextField is bound directly to a model property (TextField("Title", text: $item.title)), becoming the first responder or evaluating the text binding passes the current value back through the setter. Because SwiftData lacks value-equality checks inside property setters, focusing the text field immediately marks hasChanges as true—instantly enabling the "Save" button even though the user has not typed or edited anything.
Steps to Reproduce
- Unzip and open the attached
SwiftDataHasChanges sample project in Xcode.
- Open
SwiftDataHasChangesTests.swift.
- Click the Play button next to
test_assigningSameValue_doesNotMarkModelAsDirty to run the test.
Expected Result
Assigning item.title = item.title performs a value equality check (oldValue == newValue) and leaves item.hasChanges == false and context.hasChanges == false.
Actual Result
The unit test fails because item.hasChanges and context.hasChanges immediately evaluate to true following the reassignment.
Attachments
- SwiftDataHasChanges.zip (Contains minimal hostless unit test project reproducing the issue in
SwiftDataHasChangesTests.swift)
Keywords
No response
Prerequisites
Submission Date
2026-08-16
Status
Open
Area
SwiftData
Operating System Version
macOS 26b5
Type
Incorrect/Unexpected Behavior
Description
Setting a property on a
@Modelinstance to its existing value (e.g.,item.title = item.title) marks bothitem.hasChangesandmodelContext.hasChangesastrue.I expected SwiftData's
hasChangescheck to function like Core Data’shasPersistentChangedValues, which evaluates whether the underlying persistent storage value has actually changed before marking an object or context as dirty.Real-World Impact & Use Case
This behavior breaks standard SwiftUI form patterns when trying to enable or disable a "Save" button based on whether a model or context has pending edits.
When a SwiftUI
TextFieldis bound directly to a model property (TextField("Title", text: $item.title)), becoming the first responder or evaluating the text binding passes the current value back through the setter. Because SwiftData lacks value-equality checks inside property setters, focusing the text field immediately markshasChangesastrue—instantly enabling the "Save" button even though the user has not typed or edited anything.Steps to Reproduce
SwiftDataHasChangessample project in Xcode.SwiftDataHasChangesTests.swift.test_assigningSameValue_doesNotMarkModelAsDirtyto run the test.Expected Result
Assigning
item.title = item.titleperforms a value equality check (oldValue == newValue) and leavesitem.hasChanges == falseandcontext.hasChanges == false.Actual Result
The unit test fails because
item.hasChangesandcontext.hasChangesimmediately evaluate totruefollowing the reassignment.Attachments
SwiftDataHasChangesTests.swift)Keywords
No response
Prerequisites
FB<number>: <title>