We had this thread 8 months ago, and here's a suggested solution.
Generalising:
protocol AssignIfDifferent {}
protocol AssignIfDifferentRef: AnyObject {}
extension AssignIfDifferent {
mutating func assignIfDifferent<T: Equatable>(_ path: WritableKeyPath<Self, T>, _ newValue: T) {
if self[keyPath: path] != newValue {
self[keyPath: path] = newValue
}
}
}
extension AssignIfDifferentRef {
func assignIfDifferent<T: Equatable>(_ path: ReferenceWritableKeyPath<Self, T>, _ newValue: T) {
if self[keyPath: path] != newValue {
self[keyPath: path] = newValue
}
}
}
struct Reminder: AssignIfDifferent {
var fireDate: Date = Date.distantPast {
didSet {
print("CHANGE!")
}
}
}
class ReminderRef: AssignIfDifferentRef {
var fireDate: Date = Date.distantPast {
didSet {
print("CHANGE!")
}
}
}
var reminder = Reminder()
let sameDate = reminder.fireDate
reminder.assignIfDifferent(\.fireDate, sameDate) // no change
reminder.assignIfDifferent(\.fireDate, sameDate + 1) // change
let reminderRef = ReminderRef()
let oldDate = reminderRef.fireDate
reminderRef.assignIfDifferent(\.fireDate, oldDate) // no change
reminderRef.assignIfDifferent(\.fireDate, oldDate + 1) // change
With subscripts:
extension AssignIfDifferent {
subscript<T: Equatable>(path: KeyPath<Self, T>) -> T {
self[keyPath: path]
}
subscript<T: Equatable>(path: WritableKeyPath<Self, T>) -> T {
get { self[keyPath: path] }
set {
if self[keyPath: path] != newValue {
self[keyPath: path] = newValue
}
}
}
}
extension AssignIfDifferentRef {
subscript<T: Equatable>(path: KeyPath<Self, T>) -> T {
self[keyPath: path]
}
subscript<T: Equatable>(path: ReferenceWritableKeyPath<Self, T>) -> T {
get { self[keyPath: path] }
set {
if self[keyPath: path] != newValue {
self[keyPath: path] = newValue
}
}
}
}
The use site becomes:
reminder[\.fireDate] = sameDate // no change