Why does this assignment happen even when the values are equal?

Alternatively with the AccessTracker idea:

@dynamicMemberLookup
struct AccessTracker<Value> {
    private var value: Value

    init(_ value: Value) {
        self.value = value
    }
    subscript<T>(dynamicMember keyPath: KeyPath<Value, T>) -> T {
        value[keyPath: keyPath]
    }
    subscript<T: Equatable>(dynamicMember keyPath: WritableKeyPath<Value, T>) -> T {
        get {
            value[keyPath: keyPath]
        }
        set {
            if value[keyPath: keyPath] != newValue {
                value[keyPath: keyPath] = newValue
            }
        }
    }
}

The usage could be:

var reminder = AccessTracker(Reminder())
let old = reminder.fireDate
reminder.fireDate = old // no change
reminder.fireDate = old + 1 // change

var reminderRef = AccessTracker(ReminderRef())
reminderRef.fireDate = old // no change
reminderRef.fireDate = old + 1 // change
2 Likes