Hiding observable variables behind a read- and consume-only protocol

Since the introduction of Combine, and continuing into the I've never found a way to hide a value I want to observe behind a protocol, without introducing at least one of the following problems:

  1. It's no longer observable (@Published cannot exist in protocols, ObservableObject with difficulty), or no longer guaranteed to be observable (@Observable cannot be enforced in protocols)
  2. I'm writing pairs, like var something / var somethingPublisher: AnyPublisher - or equivalents in modern concurrency
  3. I'm allowing consumers to change or send values while they shouldn't be able, for example CurrentValueSubject

I'm mostly going for 2. currently, but it's just extremely inelegant. I can do 1. with @Observable, but if the implementer forgets to make it Observable, the protocol doesn't complain.

Being able to define all expected behavior in a protocol is - in my opinion - one of the cornerstones of Swift. We can even define global actors on protocols, guaranteeing how concurrency is expected to work.

  • How are you dealing with this?
  • Do you have any examples of how to do this better?
  • Shouldn't the language perhaps allow us to enforce this?
1 Like

For the most part I don't believe the Observable macro attaches any stable public compile-time contracts. If a product engineer chose to depend on the Observable artifacts as a compile time contract I believe this would lead to brittle code… the macro engineer building Observable should have the freedom to refactor details that are not explicitly stable or public.

I believe the "least bad" solution for now is to enforce Observability as a runtime contract… not a compile time contract. This could be unit tests that set a value and then watch for the Observation callbacks.

I do believe there is discussion about shipping a new Observed type that could be used as a compile-time contract guarantee. I'm not completely sure if that was intended to "back deploy" to earlier versions of Swift. Potentially.

1 Like