Skip to content

Commit b37dad6

Browse files
committed
Add environmentDidChange observation option
1 parent 593250d commit b37dad6

4 files changed

+68
-1
lines changed

ViewEnvironmentUI/Sources/ViewEnvironmentObserving.swift

+9
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,19 @@ public protocol ViewEnvironmentObserving: ViewEnvironmentPropagating {
7474
/// - Tag: ViewEnvironmentObserving.applyEnvironmentIfNeeded
7575
///
7676
func applyEnvironmentIfNeeded()
77+
78+
/// Called when the environment has been set for needing update, but before it has been applied.
79+
///
80+
/// This may be called frequently when compared to ``apply(environment:)`` which should only be called
81+
/// when it's appropriate to apply the environment to the backing object (e.g. `viewWillLayoutSubviews`).
82+
///
83+
func environmentDidChange()
7784
}
7885

7986
extension ViewEnvironmentObserving {
8087
public func customize(environment: inout ViewEnvironment) {}
8188

8289
public func apply(environment: ViewEnvironment) {}
90+
91+
public func environmentDidChange() {}
8392
}

ViewEnvironmentUI/Sources/ViewEnvironmentPropagatingObject.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,11 @@ extension ViewEnvironmentPropagatingObject {
225225
}
226226
}
227227

228-
setNeedsApplyEnvironment()
228+
if let observer = self as? ViewEnvironmentObserving {
229+
observer.environmentDidChange()
230+
231+
setNeedsApplyEnvironment()
232+
}
229233

230234
setNeedsEnvironmentUpdateOnAppropriateDescendants()
231235
}

ViewEnvironmentUI/Sources/ViewEnvironmentPropagationNode.swift

+12
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ public class ViewEnvironmentPropagationNode: ViewEnvironmentPropagatingObject, V
3939
didSet { setNeedsEnvironmentUpdate() }
4040
}
4141

42+
public var environmentDidChangeObserver: ((ViewEnvironment) -> Void)? {
43+
didSet { setNeedsEnvironmentUpdate() }
44+
}
45+
4246
public var applyEnvironment: (ViewEnvironment) -> Void {
4347
didSet { setNeedsEnvironmentUpdate() }
4448
}
@@ -47,11 +51,13 @@ public class ViewEnvironmentPropagationNode: ViewEnvironmentPropagatingObject, V
4751
environmentAncestor: @escaping EnvironmentAncestorProvider = { nil },
4852
environmentDescendants: @escaping EnvironmentDescendantsProvider = { [] },
4953
customizeEnvironment: @escaping (inout ViewEnvironment) -> Void = { _ in },
54+
environmentDidChange: ((ViewEnvironment) -> Void)? = nil,
5055
applyEnvironment: @escaping (ViewEnvironment) -> Void = { _ in }
5156
) {
5257
self.environmentAncestorProvider = environmentAncestor
5358
self.environmentDescendantsProvider = environmentDescendants
5459
self.customizeEnvironment = customizeEnvironment
60+
self.environmentDidChangeObserver = environmentDidChange
5561
self.applyEnvironment = applyEnvironment
5662
}
5763

@@ -71,6 +77,12 @@ public class ViewEnvironmentPropagationNode: ViewEnvironmentPropagatingObject, V
7177
customizeEnvironment(&environment)
7278
}
7379

80+
public func environmentDidChange() {
81+
guard let didChange = environmentDidChangeObserver else { return }
82+
83+
didChange(environment)
84+
}
85+
7486
public func apply(environment: ViewEnvironment) {
7587
applyEnvironment(environment)
7688
}

ViewEnvironmentUI/Tests/ViewEnvironmentObservingTests.swift

+42
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,48 @@ final class ViewEnvironmentObservingTests: XCTestCase {
164164
window.resignKey()
165165
}
166166

167+
// MARK: - environmentDidChange
168+
169+
func test_environmentDidChange() {
170+
var rootEnvironmentDidChangeCallCount = 0
171+
let rootNode = ViewEnvironmentPropagationNode(
172+
environmentDidChange: { _ in
173+
rootEnvironmentDidChangeCallCount += 1
174+
}
175+
)
176+
XCTAssertEqual(rootEnvironmentDidChangeCallCount, 0)
177+
178+
let viewController = UIViewController()
179+
rootNode.environmentDescendantsProvider = { [viewController] }
180+
181+
// Setting an environmentDescendantsProvider on ViewEnvironmentPropagationNode triggers a
182+
// setNeedsEnvironmentUpdate()
183+
XCTAssertEqual(rootEnvironmentDidChangeCallCount, 1)
184+
185+
viewController.environmentAncestorOverride = { [weak rootNode] in
186+
rootNode
187+
}
188+
189+
var leafEnvironmentDidChangeCallCount = 0
190+
let leafNode = ViewEnvironmentPropagationNode(
191+
environmentAncestor: { [weak viewController] in
192+
viewController
193+
},
194+
environmentDidChange: { _ in
195+
leafEnvironmentDidChangeCallCount += 1
196+
}
197+
)
198+
viewController.environmentDescendantsOverride = { [leafNode] }
199+
200+
XCTAssertEqual(rootEnvironmentDidChangeCallCount, 1)
201+
XCTAssertEqual(leafEnvironmentDidChangeCallCount, 0)
202+
203+
rootNode.setNeedsEnvironmentUpdate()
204+
205+
XCTAssertEqual(rootEnvironmentDidChangeCallCount, 2)
206+
XCTAssertEqual(leafEnvironmentDidChangeCallCount, 1)
207+
}
208+
167209
// MARK: - Overridden Flow
168210

169211
func test_ancestor_customFlow() {

0 commit comments

Comments
 (0)