Skip to content

Commit

Permalink
Update EnvironmentBox implementation and add Environment doc
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Ye committed Mar 10, 2024
1 parent aeb3d63 commit fe65fe0
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"location" : "https://github.com/OpenSwiftUIProject/OpenGraph",
"state" : {
"branch" : "main",
"revision" : "2435a0a02b3ab3b97799a8f387284686fe9e6a7a"
"revision" : "8baf226a2dd579d70febed94aa5bb23a04e1e56e"
}
},
{
Expand Down
191 changes: 187 additions & 4 deletions Sources/OpenSwiftUI/Data/Environment/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,139 @@
// OpenSwiftUI
//
// Audited for RELEASE_2021
// Status: Empty
// ID:
// Status: Complete
// ID: 7B48F30970137591804EEB8D0D309152

internal import OpenGraphShims

/// A property wrapper that reads a value from a view's environment.
///
/// Use the `Environment` property wrapper to read a value
/// stored in a view's environment. Indicate the value to read using an
/// ``EnvironmentValues`` key path in the property declaration. For example, you
/// can create a property that reads the color scheme of the current
/// view using the key path of the ``EnvironmentValues/colorScheme``
/// property:
///
/// @Environment(\.colorScheme) var colorScheme: ColorScheme
///
/// You can condition a view's content on the associated value, which
/// you read from the declared property's ``wrappedValue``. As with any property
/// wrapper, you access the wrapped value by directly referring to the property:
///
/// if colorScheme == .dark { // Checks the wrapped value.
/// DarkContent()
/// } else {
/// LightContent()
/// }
///
/// If the value changes, OpenSwiftUI updates any parts of your view that depend on
/// the value. For example, that might happen in the above example if the user
/// changes the Appearance settings.
///
/// You can use this property wrapper to read --- but not set --- an environment
/// value. OpenSwiftUI updates some environment values automatically based on system
/// settings and provides reasonable defaults for others. You can override some
/// of these, as well as set custom environment values that you define,
/// using the ``View/environment(_:_:)`` view modifier.
///
/// For the complete list of environment values provided by OpenSwiftUI, see the
/// properties of the ``EnvironmentValues`` structure. For information about
/// creating custom environment values, see the ``EnvironmentKey`` protocol.
///
/// ### Get an observable object
///
/// You can also use `Environment` to get an observable object from a view's
/// environment. The observable object must conform to the
/// <doc://com.apple.documentation/documentation/Observation/Observable>
/// protocol, and your app must set the object in the environment using the
/// the object itself or a key path.
///
/// To set the object in the environment using the object itself, use the
/// ``View/environment(_:)-4516h`` modifier:
///
/// @Observable
/// class Library {
/// var books: [Book] = [Book(), Book(), Book()]
///
/// var availableBooksCount: Int {
/// books.filter(\.isAvailable).count
/// }
/// }
///
/// @main
/// struct BookReaderApp: App {
/// @State private var library = Library()
///
/// var body: some Scene {
/// WindowGroup {
/// LibraryView()
/// .environment(library)
/// }
/// }
/// }
///
/// To get the observable object using its type, create a property and provide
/// the `Environment` property wrapper the object's type:
///
/// struct LibraryView: View {
/// @Environment(Library.self) private var library
///
/// var body: some View {
/// // ...
/// }
/// }
///
/// By default, reading an object from the environment returns a non-optional
/// object when using the object type as the key. This default behavior assumes
/// that a view in the current hierarchy previously stored a non-optional
/// instance of the type using the ``View/environment(_:)-4516h`` modifier. If
/// a view attempts to retrieve an object using its type and that object isn't
/// in the environment, OpenSwiftUI throws an exception.
///
/// In cases where there is no guarantee that an object is in the environment,
/// retrieve an optional version of the object as shown in the following code.
/// If the object isn't available the environment, OpenSwiftUI returns `nil`
/// instead of throwing an exception.
///
/// @Environment(Library.self) private var library: Library?
///
/// ### Get an observable object using a key path
///
/// To set the object with a key path, use the ``View/environment(_:_:)``
/// modifier:
///
/// @Observable
/// class Library {
/// var books: [Book] = [Book(), Book(), Book()]
///
/// var availableBooksCount: Int {
/// books.filter(\.isAvailable).count
/// }
/// }
///
/// @main
/// struct BookReaderApp: App {
/// @State private var library = Library()
///
/// var body: some Scene {
/// WindowGroup {
/// LibraryView()
/// .environment(\.library, library)
/// }
/// }
/// }
///
/// To get the object, create a property and specify the key path:
///
/// struct LibraryView: View {
/// @Environment(\.library) private var library
///
/// var body: some View {
/// // ...
/// }
/// }
///
@frozen
@propertyWrapper
public struct Environment<Value>: DynamicProperty {
Expand All @@ -21,11 +149,46 @@ public struct Environment<Value>: DynamicProperty {
@usableFromInline
var content: Content

/// Creates an environment property to read the specified key path.
///
/// Don’t call this initializer directly. Instead, declare a property
/// with the ``Environment`` property wrapper, and provide the key path of
/// the environment value that the property should reflect:
///
/// struct MyView: View {
/// @Environment(\.colorScheme) var colorScheme: ColorScheme
///
/// // ...
/// }
///
/// OpenSwiftUI automatically updates any parts of `MyView` that depend on
/// the property when the associated environment value changes.
/// You can't modify the environment value using a property like this.
/// Instead, use the ``View/environment(_:_:)`` view modifier on a view to
/// set a value for a view hierarchy.
///
/// - Parameter keyPath: A key path to a specific resulting value.
@inlinable
public init(_ keyPath: KeyPath<EnvironmentValues, Value>) {
content = .keyPath(keyPath)
}

/// The current value of the environment property.
///
/// The wrapped value property provides primary access to the value's data.
/// However, you don't access `wrappedValue` directly. Instead, you read the
/// property variable created with the ``Environment`` property wrapper:
///
/// @Environment(\.colorScheme) var colorScheme: ColorScheme
///
/// var body: some View {
/// if colorScheme == .dark {
/// DarkContent()
/// } else {
/// LightContent()
/// }
/// }
///
@inlinable
public var wrappedValue: Value {
switch content {
Expand Down Expand Up @@ -70,7 +233,27 @@ private struct EnvironmentBox<Value>: DynamicPropertyBox {

func destroy() {}
func reset() {}
func update(property: inout Environment<Value>, phase: _GraphInputs.Phase) -> Bool {
fatalError("TODO")
mutating func update(property: inout Environment<Value>, phase _: _GraphInputs.Phase) -> Bool {
guard case let .keyPath(propertyKeyPath) = property.content else {
return false
}
let (environment, environmentChanged) = _environment.changedValue(options: [])
let keyPathChanged = (propertyKeyPath != keyPath)
if keyPathChanged { keyPath = propertyKeyPath }
let valueChanged: Bool
if keyPathChanged || environmentChanged {
let newValue = environment[keyPath: propertyKeyPath]
if let value, compareValues(value, newValue) {
valueChanged = false
} else {
value = newValue
valueChanged = true
}
} else {
valueChanged = false
}
let value: Value = self.value!
property.content = .value(value)
return valueChanged
}
}

0 comments on commit fe65fe0

Please sign in to comment.