Looking through some WWDC26 sample code I came across something that was difficult to understand:
activity.token = withContinuousObservation(options: .didSet) { event in
_ = activity.name
_ = activity.isComplete
if event.matches(\Activity.name) {
activity.dateEdited = .now
}
if event.matches(\Activity.isComplete) {
activity.dateEdited = .now
activity.trip?.isComplete = activity.trip?.activities.isEmpty == false
&& activity.trip?.activities.allSatisfy { $0.isComplete } == true
}
}
It took me a moment to understand that the _ = activity.name and _ = activity.isComplete are necessary to start observing those properties, and then later the observation is concerned about which property was changed via the Event.matches method. That means if you forget to do those _ = lines of code you will break this feature.
This is a little surprising because we are used to having the "magic" of observable just work. You access something inside the trailing closure, and it's observed. But here, matches(KeyPath) does not start observation for that property, nor can it. And I think it's pretty reasonable that 6 months from now you would forget what those lines meant, and then figure it's old debug code left over that should be deleted.
Is there room for an API that can check the event for the mutation of a particular property, and register an access with the registrar? Something like this:
activity.token = withContinuousObservation(options: .didSet) { event in
if event.matches(activity, \.name) {
activity.dateEdited = .now
}
if event.matches(activity, \.isComplete) {
activity.dateEdited = .now
activity.trip?.isComplete = activity.trip?.activities.isEmpty == false
&& activity.trip?.activities.allSatisfy { $0.isComplete } == true
}
}
The act of checking matches(activity, \.name) will also register an access in the registrar so that you no longer need to know to add those _ = lines. And I could still see a use case for matches(_:) for when you don't care about which object had the mutation, just that the mutation occurred.