1616
1717import ViewEnvironment
1818
19+ /// `ViewEnvironmentPropagating` allows an environment propagation node to observe updates to the
20+ /// `ViewEnvironment` as it flows through the node hierarchy and have
21+ /// the environment applied to the node.
22+ ///
23+ /// For example, for a `UIViewController` hierarchy observing `ViewEnvironment`:
24+ /// ```swift
25+ /// final class MyViewController:
26+ /// UIViewController, ViewEnvironmentPropagating
27+ /// {
28+ /// override func viewWillLayoutSubviews() {
29+ /// super.viewWillLayoutSubviews()
30+ ///
31+ /// // You _must_ call this function in viewWillLayoutSubviews()
32+ /// applyEnvironmentIfNeeded()
33+ /// }
34+ ///
35+ /// func apply(environment: ViewEnvironment) {
36+ /// // Apply values from the environment to your view controller (e.g. a theme)
37+ /// }
38+ ///
39+ /// // If you'd like to override values in the environment you can provide them here. If you'd
40+ /// // like to just inherit the context from above there is no need to implement this function.
41+ /// func customize(environment: inout ViewEnvironment) {
42+ /// environment.traits.mode = .dark
43+ /// }
44+ /// }
45+ /// ```
46+ ///
47+ /// - Important: `UIViewController` and `UIView` conformers _must_ call ``applyEnvironmentIfNeeded()-3bamq``
48+ /// in `viewWillLayoutSubviews()` and `layoutSubviews()` respectively.
49+ ///
1950public protocol ViewEnvironmentPropagating {
2051 /// Calling this will flag this node for needing to update the `ViewEnvironment`. For `UIView`/`UIViewController`,
2152 /// this will occur on the next layout pass (`setNeedsLayout` will be called on the caller's behalf).
@@ -39,6 +70,10 @@ public protocol ViewEnvironmentPropagating {
3970 /// ``ViewEnvironmentPropagatingObject/environmentAncestorOverride`` property. If no override is present, the
4071 /// return value will be `parent ?? presentingViewController`/`superview`.
4172 ///
73+ /// If the value of the ancestor is `nil`, by default, ancestors will not call notify this node of needing an
74+ /// environment update as it changes. This allows a node to effectively act as a root node when needed (e.g.
75+ /// bridging from other propagation systems like WorkflowUI).
76+ ///
4277 @_spi ( ViewEnvironmentWiring)
4378 var environmentAncestor : ViewEnvironmentPropagating ? { get }
4479
@@ -54,34 +89,6 @@ public protocol ViewEnvironmentPropagating {
5489 ///
5590 @_spi ( ViewEnvironmentWiring)
5691 var environmentDescendants : [ ViewEnvironmentPropagating ] { get }
57-
58- /// The `ViewEnvironment` that is flowing through the propagation hierarchy.
59- ///
60- /// If you'd like to provide overrides for the environment as it flows through a node, you should conform to
61- /// `ViewEnvironmentObserving` and provide those overrides in `customize(environment:)`. E.g.:
62- /// ```swift
63- /// func customize(environment: inout ViewEnvironment) {
64- /// environment.traits.mode = .dark
65- /// }
66- /// ```
67- ///
68- /// By default, this property gets the environment by recursively walking to the root of the
69- /// propagation path, and applying customizations on the way back down. You may override this
70- /// property instead if you want to completely interrupt the propagation flow and replace the
71- /// environment. You can get the default value that would normally be propagated by calling
72- /// `_defaultViewEnvironment`.
73- ///
74- /// If you'd like to update the return value of this variable and have those changes propagated through the
75- /// propagation hierarchy, conform to `ViewEnvironmentObserving` and call ``setNeedsEnvironmentUpdate()`` and wait
76- /// for the system to call `apply(context:)` when appropriate (e.g. on the next layout pass for
77- /// `UIViewController`/`UIView` subclasses).
78- ///
79- /// - Important: `UIViewController` and `UIView` conformers _must_ call
80- /// ``ViewEnvironmentObserving/applyEnvironmentIfNeeded()-8gr5k`` in `viewWillLayoutSubviews()` and
81- /// `layoutSubviews()` respectively.
82- ///
83- @_spi ( ViewEnvironmentWiring)
84- var environment : ViewEnvironment { get }
8592}
8693
8794extension ViewEnvironmentPropagating {
@@ -99,7 +106,7 @@ extension ViewEnvironmentPropagating {
99106 /// propagation path, and applying customizations on the way back down. You may override this
100107 /// property instead if you want to completely interrupt the propagation flow and replace the
101108 /// environment. You can get the default value that would normally be propagated by calling
102- /// `_defaultViewEnvironment `.
109+ /// `_defaultEnvironment `.
103110 ///
104111 /// If you'd like to update the return value of this variable and have those changes propagated through the
105112 /// propagation hierarchy, conform to `ViewEnvironmentObserving` and call ``setNeedsEnvironmentUpdate()`` and wait
@@ -111,7 +118,7 @@ extension ViewEnvironmentPropagating {
111118 /// `layoutSubviews()` respectively.
112119 ///
113120 public var environment : ViewEnvironment {
114- _defaultViewEnvironment
121+ ( self as? ViewEnvironmentObserving ) ? . _environmentOverride ?? _defaultEnvironment
115122 }
116123
117124 /// The default `ViewEnvironment` returned by ``environment``.
@@ -122,15 +129,30 @@ extension ViewEnvironmentPropagating {
122129 /// You should only need to access this value if you are overriding ``environment``
123130 /// and want to conditionally return the default.
124131 @_spi ( ViewEnvironmentWiring)
125- public var _defaultViewEnvironment : ViewEnvironment {
126- var environment = environmentAncestor? . environment
127- ?? . empty
132+ public var _defaultEnvironment : ViewEnvironment {
133+ var environment : ViewEnvironment = {
134+ guard let ancestor = environmentAncestor else {
135+ return . empty
136+ }
137+
138+ if let observing = ancestor as? ViewEnvironmentObserving {
139+ return observing. environment
140+ }
128141
129- ( self as? ViewEnvironmentCustomizing ) ? . customize ( environment: & environment)
142+ return ancestor. _defaultEnvironment
143+ } ( )
144+
145+ if let observing = self as? ViewEnvironmentObserving {
146+ observing. customize ( environment: & environment)
147+ }
130148
131149 return environment
132150 }
133151
152+ /// Notifies all appropriate descendants that the environment needs update.
153+ ///
154+ /// If a descendant's ancestor is `nil` it will not be notified of needing update.
155+ ///
134156 @_spi ( ViewEnvironmentWiring)
135157 public func setNeedsEnvironmentUpdateOnAppropriateDescendants( ) {
136158 for descendant in environmentDescendants {
0 commit comments