How does an @ObjectBinding wrapper know what view it's in?

When we write

struct MyView : View {
    @ObjectBinding var pref1: SomeBindableModel
   ...
}

my understanding is that when the didChange publisher of pref1 fires, the MyView instance it happens to live in gets invalidated. But how the heck does pref1 manage to know that it lives in a specific instance of MyView? My very (limited) understanding of property wrappers is that when constructing pref1, there is no opportunity for that function to get at "self" (i.e. the class the property lives in).

If that's not true, I'd love to know how to do it myself. If it is true, I'm mystified at how the linkage between pref1 and its enclosing class gets established.

I'm sure this is counter to everything, but I'm trying to make stuff work without having to always insert @ObjectBinding in the enclosing view class. It seems as though it is impossible to manufacture a binding unless you start from a view that has an @ObjectBinding property...

1 Like

Hi @davidbaraff,

As far as I know:

  1. A BindableObject must have a didChange Publisher
  2. When you declare a BindableObject on a view, you either pass the instance to its builder function (when its parent have reference to it) OR you pass it as EnvironmentObject when the view is created with .environmentObject(:) modifier.

My assumption is that the view subscribes to the didChange publisher/subject in the above mentioned methods and re-renders if someone calls send on didChange.

I'm also learning the framework, so these are assumptions, but it make sense for me :)

Cheers,
Peter

2 Likes

The base class view doesn’t know about the instance variables you’ve added (the BindableObject properties).
The view you wrote likewise doesn’t have any wiring to connect them, unless the compiler is adding them under the hood.

And the BindableObject property instance itself doesn’t get a reference to its enclosing view.

So I have no doubt they talk, the question is: “who/where/how was the introduction between them made to establish the wiring?”

Come one, people in the know, give this question some love and give us a clue as to what the hidden magic is.

Sorry, I think, I misunderstood your question. :slight_smile:

The question is about, how the view knows that it should expect a BindableObject, right?
Digging in the documentation of @ObjectBinding, it states that the view automatically registers to this binding because of the DynamicViewProperty behaviour.

Unfortunately the doc doesn't tell more :S Let's wait then for a swiftUI-insider.

There was some mention in one of the WWDC sessions on SwiftUI that the getter methods for the various SwiftUI property wrappers keep track of when you read from them. So if you read the value of an @ObjectBinding property from within your body implementation, then SwiftUI knows that your view is using it, and can watch for changes accordingly.

Update: It's in Data Flow Through SwiftUI, starting at about 8:40.

This is how I do it, but I think it is actually more complicated than necessary (because I decouple the Views persistent state from the View storage in the render tree): https://github.com/swiftwebui/SwiftWebUI/blob/master/Sources/SwiftWebUI/Properties/ObjectBinding.swift#L12

Essentially when body runs, the framework scans the View for DynamicViewProperty objects. If it finds such, it assigns context and a persistent "value holder" to them. (that's why if you access them from outside body, you get the warning, it lacks the context). In the case of the ObjectBinding, that "value holder" contains the subscription.

So you use mirroring (swift introspection to find properties that conform to the dynamic view protocol, and having found them, wire up the dependencies?

Yes (not using Swift's builtin Mirror introspection but the Reflection library). The reflection part is located over here: SwiftWebUI/ComponentReflection.swift at master · SwiftWebUI/SwiftWebUI · GitHub

BTW: This is a related thread: DynamicViewProperty

https://forums.swift.org/t/dynamicviewproperty/25627/7

So this is my implementation: https://github.com/swiftwebui/SwiftWebUI/blob/master/Sources/SwiftWebUI/Properties/DynamicViewProperty.swift#L9 11
I'm using Reflection (the lib) to find the offset positions of the properties in the View.

To make the View's value "mutable" one just casts the pointer to a mutable one. Which explains why this works even if the properties are defined as let. Not sure whether that is really sound (optimiser might layout the properties differently?), but I guess the layout should be stable as part of the "ABI".