In particular, I'm looking at the feature of @Environment where variables can be set on a View, but then all sub-Views have access to the same @Environment. And where different View can have different environment values. I'd like to make a property wrapper that this similar behavior.
I've forgotten about that post, honestly. I'll come around to documenting my findings and posting them.
How It Works
Here's how I think it works after digging around under the hood and experimenting (none of it maintains any global state):
EnvironmentValues
The EnvironmentValues structure has an internal init() that creates a default instance. It has a single private var everyValue: [ObjectIdentifier: Any] inside where the key is constructed using the metadata object of the type conforming to the EnvironmentKey protocol (e.g. Key) and the value is a type-erased instance of Key.Value (self.everyValue[.init(Key.self), default: Key.defaultValue] as! Key.Value). The public subscript merely provides type-safe access to that dictionary.
Environment
The Environment structure has an internal enum Storage with case keyPath(KeyPath<EnvironmentValues, Value>) and case value(Value). It also has a func resolve(with environment: EnvironmentValues) which applies the key path and changes the storage to case value. The wrappedValue returns the direct value or the default value by resolving the key path against a default environment.
View
The View protocol requires the conforming type to be a value type because it's relying on every view to be deep-copyable. If you conform a reference type to it, you'll get an assertion failure.
Before accessing the view's body, the view is copied and using hidden and more powerful reflection, all its @Environment properties are found and their resolve(with:) called with the current environment. After resolving all environment values, the body is accessed and the process recurses into subviews.
The .environment(_:_:) modifier simply wraps the affected view with a view modifier that accesses the current environment and sets the value with the provided key path.
Try This At Home
I've abstracted away the heterogeneous container pattern used by EnvironmentValues into AttributeKit and abstracted away access to the hidden and more powerful reflection into IntrospectionKit.
Wow, okay that's really deep! Thanks for that really detailed explanation! Reflection was not in my mental toolbox before. I'll have to take a look and see if this can be useful for me. Thank you so much!