I’m trying to figure out how DynamicViewProperty in SwiftUI works, that mechanism is something I want as well :-> Does that use a custom reflection to make it work?

struct MyOwnCoolViewProperty : DynamicViewProperty {
  var gotCalled = 0
  mutating func update() { gotCalled += 1 }
struct MyView: View {
  var prop = MyOwnCoolViewProperty()
  var body ....

This is calling update before the body property is accessed. But how? 🙂

It is easy enough to get the dynamic properties using something like:

let dynamicProps = Mirror(reflecting: view).children.compactMap {
  $0.value as? DynamicViewProperty

But that doesn't allow you to call a mutating function. Are they using custom reflection to achieve that, or doesn't this involve reflection at all?


On a high level it seems to be using much more than reflection, its actually directly calling into Swift runtime. Logic for this is implemented in private AttributeGraph framework written in C++, that weakly imports a lot of Swift runtime symbols.
This has interesting side effects, you can make the property ‘let’ and it will still be updated :upside_down_face:

I’ve looked at it briefly, so maybe someone will be able to provide more details.

Yes, SwiftUI uses Swift runtime metadata to reflect over views.


Calling into the Swift runtime is reflection :-) It just seems that there is no Swift API for that.

For now I solved it in a different way, which funny enough also has the same let sideeffect ;-)

P.S.: I think the C++ part is actually unrelated, that "just" seems to be the structure for the rendering tree.

Right, just looking at the symbols it references, they seem to be doing more than just iterating over properties and calling Swift functions

Taking this opportunity to suggest that Swift.Mirror should get some love


So this is my implementation: https://github.com/swiftwebui/SwiftWebUI/blob/master/Sources/SwiftWebUI/Properties/DynamicViewProperty.swift#L9
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".

1 Like