Unfortunately this proposal doesn't address the concerns raised in the last pitch. I'll try and summarise those concerns once again here.
Asynchronous Sequences are unsuitable for same-actor observation
The biggest issue for me is the use of asynchronous sequences to deliver observation events. While an asynchronous sequence is a suitable mechanism for inter-actor observation, it seems unsuitable for intra-actor observation.
In other words, while the asynchronous sequence APIs would be great for observing events occurring on different actors, using them to observe events on a graph of objects that exist on the same actor (say the @MainActor
) will quickly see the manifestation of race conditions, state desynchronisation, and the above mentioned 'flickery UIs'.
Some of the ways that this might manifest:
And as using 'Observability' to observe events on a graph of @MainActor
objects seems like it would be an expected and typical use case, this seems like an unacceptable limitation.
This is because, no matter how you roll it, the use of an asynchronous sequence imposes at minimum a same-actor hop between the dispatch of an observation event and the delivery of that observation event – and often includes a round-trip via the shared thread pool. (In fact, there's a WWDC talk recommending against exactly this sort of frequent actor-hoping somewhere.)
One argument in the prior pitch was that 'that's what the ObservatioinTracking
mechanism is for'. But the 'ObservationTracking' mechanism has an API that isn't suitable for general purpose observability. It's been custom designed to trigger view updates for consumption by a UI framework such as SwiftUI. It's not the API most programmers will be reaching for to do their own observations. So internally, SwiftUI has the API they need, but externally we're left fighting with an unsuitable API.
And so it's a new burden that we'll be placing on programmers. That of ensuring they've considered the complexity of concurrent programming even when performing same actor updates. I think that this will be a surprise and unintuitive to many. It's an unnecessary burden.
Simplifying and exaggerating this to illustrate my point: it's as if we've decided that synchronous functions are unnecessary as we can do everything a synchronous function can with an asynchronous function.
We can't.