Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ struct GreetingGeneratorApp: App {

Toggle("Selectable Greeting", active: $isGreetingSelectable)
if let latest = greetings.last {
Text(latest)
EnvironmentDisplay()
.environment(key: TestKey.self, value: latest)
.padding(.top, 5)
.textSelectionEnabled(isGreetingSelectable)

Expand All @@ -51,3 +52,15 @@ struct GreetingGeneratorApp: App {
}
}
}

struct EnvironmentDisplay: View {
@Environment(TestKey.self) var value: String?
var body: some View {
Text(value ?? "nil")
}
}

struct TestKey: EnvironmentKey {
typealias Value = String?
static let defaultValue: Value = nil
}
16 changes: 13 additions & 3 deletions Sources/SwiftCrossUI/Environment/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,26 @@
/// ```
@propertyWrapper
public struct Environment<Value>: DynamicProperty {
var keyPath: KeyPath<EnvironmentValues, Value>
var keyPath: KeyPath<EnvironmentValues, Value>?
var environmentKey: (any EnvironmentKey.Type)?
var value: Box<Value?>

public func update(
with environment: EnvironmentValues,
previousValue: Self?
) {
value.value = environment[keyPath: keyPath]
if let keyPath {
value.value = environment[keyPath: keyPath]
} else if let environmentKey {
value.value = (environment[environmentKey] as! Value)
}
}

public var wrappedValue: Value {
guard let value = value.value else {
fatalError(
"""
Environment value \(keyPath) used before initialization. Don't \
Environment value \(keyPath.debugDescription) used before initialization. Don't \
use @Environment properties before SwiftCrossUI requests the \
view's body.
"""
Expand All @@ -63,4 +68,9 @@ public struct Environment<Value>: DynamicProperty {
self.keyPath = keyPath
value = Box(value: nil)
}

public init<Key: EnvironmentKey>(_ type: Key.Type) where Value == Key.Value {
self.environmentKey = type
self.value = Box(value: nil)
}
}
8 changes: 8 additions & 0 deletions Sources/SwiftCrossUI/Environment/EnvironmentValues.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ public struct EnvironmentValues {
environment[keyPath: keyPath] = newValue
return environment
}

/// Returns a copy of the environment with the specified key set to the
/// provided new value.
public func with<T: EnvironmentKey>(key: T.Type, value: T.Value) -> Self {
var environment = self
environment[key] = value
return environment
}
}

/// A key that can be used to extend the environment with new properties.
Expand Down
18 changes: 18 additions & 0 deletions Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,21 @@ package struct EnvironmentModifier<Child: View>: View {
)
}
}

extension View {
/// Modifies the environment of the View its applied to.
public func environment<T: EnvironmentKey>(key: T.Type, value: T.Value) -> some View {
EnvironmentModifier(self) { environment in
environment.with(key: key, value: value)
}
}

/// Modifies the environment of the View its applied to
public func environment<T>(_ keyPath: WritableKeyPath<EnvironmentValues, T>, _ newValue: T)
-> some View
{
EnvironmentModifier(self) { environment in
environment.with(keyPath, newValue)
}
}
}
Loading