11//
22// Environment.swift
3- // OpenSwiftUI
3+ // OpenSwiftUICore
44//
5- // Audited for 3 .5.2
5+ // Audited for 6 .5.4
66// Status: Complete
7- // ID: 7B48F30970137591804EEB8D0D309152
7+ // ID: 7B48F30970137591804EEB8D0D309152 (SwiftUI)
8+ // ID: 24E0E088473ED74681D096110CC5FC9A (SwiftUICore)
89
910import OpenAttributeGraphShims
1011#if OPENSWIFTUI_SWIFT_LOG
@@ -13,6 +14,8 @@ public import Logging
1314public import os
1415#endif
1516
17+ // MARK: - Environment
18+
1619/// A property wrapper that reads a value from a view's environment.
1720///
1821/// Use the `Environment` property wrapper to read a value
@@ -46,7 +49,7 @@ public import os
4649///
4750/// For the complete list of environment values provided by OpenSwiftUI, see the
4851/// properties of the ``EnvironmentValues`` structure. For information about
49- /// creating custom environment values, see the ``EnvironmentKey `` protocol .
52+ /// creating custom environment values, see the ``Entry() `` macro .
5053///
5154/// ### Get an observable object
5255///
@@ -57,7 +60,7 @@ public import os
5760/// the object itself or a key path.
5861///
5962/// To set the object in the environment using the object itself, use the
60- /// ``View/environment(_:)-4516h `` modifier:
63+ /// ``View/environment(_:)`` modifier:
6164///
6265/// @Observable
6366/// class Library {
@@ -94,7 +97,7 @@ public import os
9497/// By default, reading an object from the environment returns a non-optional
9598/// object when using the object type as the key. This default behavior assumes
9699/// that a view in the current hierarchy previously stored a non-optional
97- /// instance of the type using the ``View/environment(_:)-4516h `` modifier. If
100+ /// instance of the type using the ``View/environment(_:)`` modifier. If
98101/// a view attempts to retrieve an object using its type and that object isn't
99102/// in the environment, OpenSwiftUI throws an exception.
100103///
@@ -141,18 +144,25 @@ public import os
141144/// }
142145/// }
143146///
147+ @available ( OpenSwiftUI_v1_0, * )
144148@frozen
145149@propertyWrapper
146150public struct Environment < Value> : DynamicProperty {
151+
147152 @usableFromInline
148153 @frozen
149- enum Content {
154+ internal enum Content : @unchecked Sendable {
155+
156+ /// A key path describing how to dereference the view's current
157+ /// environment to produce the linked value.
150158 case keyPath( KeyPath < EnvironmentValues , Value > )
159+
160+ /// The view's current value of the environment property.
151161 case value( Value )
152162 }
153163
154164 @usableFromInline
155- var content : Content
165+ internal var content : Content
156166
157167 /// Creates an environment property to read the specified key path.
158168 ///
@@ -194,7 +204,6 @@ public struct Environment<Value>: DynamicProperty {
194204 /// }
195205 /// }
196206 ///
197- // Audited for RELEASE_2023
198207 @inlinable
199208 public var wrappedValue : Value {
200209 switch content {
@@ -231,7 +240,7 @@ public struct Environment<Value>: DynamicProperty {
231240 }
232241
233242 @usableFromInline
234- func error( ) -> Never {
243+ internal func error( ) -> Never {
235244 preconditionFailure ( " Reading Environment< \( Value . self) > outside View.body " )
236245 }
237246
@@ -241,52 +250,96 @@ public struct Environment<Value>: DynamicProperty {
241250 fieldOffset: Int ,
242251 inputs: inout _GraphInputs
243252 ) {
244- buffer. append (
245- EnvironmentBox < Value > (
246- environment: inputs. environment
247- ) ,
248- fieldOffset: fieldOffset
249- )
253+ if Value . self == EnvironmentValues . self {
254+ buffer. append (
255+ FullEnvironmentBox ( environment: inputs. environment) ,
256+ fieldOffset: fieldOffset
257+ )
258+ } else {
259+ buffer. append (
260+ EnvironmentBox < Value > ( environment: inputs. environment) ,
261+ fieldOffset: fieldOffset
262+ )
263+ }
250264 }
251265}
252266
253267@available ( OpenSwiftUI_v1_0, * )
254268extension Environment : Sendable where Value: Sendable { }
255269
256- private struct EnvironmentBox < Value> : DynamicPropertyBox {
270+ // MARK: - FullEnvironmentBox
271+
272+ private struct FullEnvironmentBox : DynamicPropertyBox {
257273 @Attribute var environment : EnvironmentValues
258- var keyPath : KeyPath < EnvironmentValues , Value > ?
259- var value : Value ?
260-
261- init ( environment: Attribute < EnvironmentValues > ) {
262- _environment = environment
263- keyPath = nil
264- value = nil
265- }
266-
267- func destroy( ) { }
268- func reset( ) { }
269- mutating func update( property: inout Environment < Value > , phase _: _GraphInputs . Phase ) -> Bool {
274+ var keyPath : KeyPath < EnvironmentValues , EnvironmentValues > ?
275+ var value : EnvironmentValues ?
276+ var tracker : PropertyList . Tracker = . init( )
277+
278+ typealias Property = Environment < EnvironmentValues >
279+
280+ mutating func update( property: inout Property , phase _: ViewPhase ) -> Bool {
270281 guard case let . keyPath( propertyKeyPath) = property. content else {
271282 return false
272283 }
273- let ( environment, environmentChanged) = _environment . changedValue ( )
284+ let ( environment, environmentChanged) = $environment . changedValue ( )
274285 let keyPathChanged = ( propertyKeyPath != keyPath)
275- if keyPathChanged { keyPath = propertyKeyPath }
286+ if keyPathChanged {
287+ keyPath = propertyKeyPath
288+ }
276289 let valueChanged : Bool
277290 if keyPathChanged || environmentChanged {
278291 let newValue = environment [ keyPath: propertyKeyPath]
292+ if let value, !tracker. hasDifferentUsedValues ( environment. plist) {
293+ valueChanged = false
294+ } else {
295+ tracker. reset ( )
296+ tracker. initializeValues ( from: newValue. plist)
297+ value = EnvironmentValues ( newValue. plist, tracker: tracker)
298+ valueChanged = true
299+ }
300+ } else {
301+ valueChanged = false
302+ }
303+ property. content = . value( value!)
304+ return valueChanged
305+ }
306+ }
307+
308+ // MARK: - EnvironmentBox
309+
310+ private struct EnvironmentBox < Value> : DynamicPropertyBox {
311+ @Attribute var environment : EnvironmentValues
312+ var keyPath : KeyPath < EnvironmentValues , Value > ?
313+ var value : Value ?
314+ var hadObservation : Bool = false
315+
316+ typealias Property = Environment < Value >
317+
318+ mutating func update( property: inout Property , phase _: ViewPhase ) -> Bool {
319+ guard case let . keyPath( propertyKeyPath) = property. content else {
320+ return false
321+ }
322+ let ( environment, environmentChanged) = $environment. changedValue ( )
323+ let keyPathChanged = ( propertyKeyPath != keyPath)
324+ var valueChanged = environmentChanged
325+ if keyPathChanged {
326+ keyPath = propertyKeyPath
327+ valueChanged = true
328+ }
329+ if keyPathChanged || environmentChanged || hadObservation {
330+ let ( newValue, accessList) = _withObservation {
331+ environment [ keyPath: propertyKeyPath]
332+ }
333+ hadObservation = accessList != nil
279334 if let value, compareValues ( value, newValue) {
280335 valueChanged = false
281336 } else {
282337 value = newValue
283- valueChanged = true
284338 }
285339 } else {
286340 valueChanged = false
287341 }
288- let value : Value = self . value!
289- property. content = . value( value)
342+ property. content = . value( value!)
290343 return valueChanged
291344 }
292345}
0 commit comments