diff --git a/Sources/OpenSwiftUICore/Modifier/CustomViewModifier.swift b/Sources/OpenSwiftUICore/Modifier/CustomViewModifier.swift
index c3037414b..13da69f7c 100644
--- a/Sources/OpenSwiftUICore/Modifier/CustomViewModifier.swift
+++ b/Sources/OpenSwiftUICore/Modifier/CustomViewModifier.swift
@@ -161,13 +161,11 @@ extension ViewModifier {
inputs: inout _GraphInputs,
fields: DynamicPropertyCache.Fields
) -> (_GraphValue
, _DynamicPropertyBuffer?) {
- let kind = Metadata(Self.self).kind
- switch kind {
- case .struct, .enum, .optional, .tuple:
- return ModifierBodyAccessor().makeBody(container: modifier, inputs: &inputs, fields: fields)
- default:
+ guard Metadata(Self.self).isValueType else {
preconditionFailure("view modifiers must be value types: \(Self.self)")
}
+ let accessor = ModifierBodyAccessor()
+ return accessor.makeBody(container: modifier, inputs: &inputs, fields: fields)
}
}
diff --git a/Sources/OpenSwiftUICore/Modifier/ViewModifier/EnvironmentalModifier.swift b/Sources/OpenSwiftUICore/Modifier/ViewModifier/EnvironmentalModifier.swift
new file mode 100644
index 000000000..d8da4c589
--- /dev/null
+++ b/Sources/OpenSwiftUICore/Modifier/ViewModifier/EnvironmentalModifier.swift
@@ -0,0 +1,143 @@
+//
+// EnvironmentalModifier.swift
+// OpenSwiftUICore
+//
+// Audited for 6.5.4
+// Status: Complete
+// ID: A1B6966B83442495FADFE75F475ECBE2 (SwiftUICore)
+
+import OpenAttributeGraphShims
+
+// MARK: - EnvironmentalModifier
+
+/// A modifier that must resolve to a concrete modifier in an environment before
+/// use.
+@available(OpenSwiftUI_v1_0, *)
+public protocol EnvironmentalModifier: ViewModifier where Body == Never {
+ /// The type of modifier to use after being resolved.
+ associatedtype ResolvedModifier: ViewModifier
+
+ /// Resolve to a concrete modifier in the given `environment`.
+ func resolve(in environment: EnvironmentValues) -> ResolvedModifier
+
+ @available(OpenSwiftUI_v3_0, *)
+ static var _requiresMainThread: Bool { get }
+
+ @available(OpenSwiftUI_v5_0, *)
+ static var _tracksEnvironmentDependencies: Bool { get }
+}
+
+// MARK: - EnvironmentalModifier Default Implementations
+
+@available(OpenSwiftUI_v1_0, *)
+extension EnvironmentalModifier {
+ nonisolated public static func _makeView(
+ modifier: _GraphValue,
+ inputs: _ViewInputs,
+ body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
+ ) -> _ViewOutputs {
+ var inputs = inputs
+ let fields = DynamicPropertyCache.fields(of: Self.self)
+ let (resolvedModifier, buffer) = makeResolvedModifier(modifier: modifier, inputs: &inputs.base, fields: fields)
+ let outputs = ResolvedModifier.makeDebuggableView(
+ modifier: resolvedModifier,
+ inputs: inputs,
+ body: body
+ )
+ if let buffer {
+ buffer.traceMountedProperties(to: modifier, fields: fields)
+ }
+ return outputs
+ }
+
+ nonisolated public static func _makeViewList(
+ modifier: _GraphValue,
+ inputs: _ViewListInputs,
+ body: @escaping (_Graph, _ViewListInputs) -> _ViewListOutputs
+ ) -> _ViewListOutputs {
+ var inputs = inputs
+ let fields = DynamicPropertyCache.fields(of: Self.self)
+ let (resolvedModifier, buffer) = makeResolvedModifier(modifier: modifier, inputs: &inputs.base, fields: fields)
+ let outputs = ResolvedModifier.makeDebuggableViewList(
+ modifier: resolvedModifier,
+ inputs: inputs,
+ body: body
+ )
+ if let buffer {
+ buffer.traceMountedProperties(to: modifier, fields: fields)
+ }
+ return outputs
+ }
+
+ nonisolated private static func makeResolvedModifier(
+ modifier: _GraphValue,
+ inputs: inout _GraphInputs,
+ fields: DynamicPropertyCache.Fields
+ ) -> (_GraphValue, _DynamicPropertyBuffer?) {
+ guard Metadata(Self.self).isValueType else {
+ preconditionFailure("Environmental modifiers must be value types: \(Self.self)")
+ }
+ var fields = fields
+ if !fields.behaviors.contains(.requiresMainThread), !_requiresMainThread {
+ fields.behaviors.formUnion(
+ !fields.behaviors.contains(.allowsAsync) && isLinkedOnOrAfter(.v4) ? .allowsAsync : []
+ )
+ }
+ let accessor = EnvironmentalBodyAccessor(
+ environment: inputs.environment,
+ tracksDependencies: _tracksEnvironmentDependencies
+ )
+ return accessor.makeBody(container: modifier, inputs: &inputs, fields: fields)
+ }
+
+ @available(OpenSwiftUI_v3_0, *)
+ public static var _requiresMainThread: Bool {
+ true
+ }
+
+ @available(OpenSwiftUI_v5_0, *)
+ public static var _tracksEnvironmentDependencies: Bool {
+ true
+ }
+}
+
+// MARK: - EnvironmentalBodyAccessor
+
+private struct EnvironmentalBodyAccessor: BodyAccessor where V: EnvironmentalModifier {
+ typealias Container = V
+
+ typealias Body = V.ResolvedModifier
+
+ @Attribute
+ var environment: EnvironmentValues
+ let tracker: PropertyList.Tracker
+ let tracksDependencies: Bool
+
+ init(
+ environment: Attribute,
+ tracksDependencies: Bool
+ ) {
+ self._environment = environment
+ self.tracker = .init()
+ self.tracksDependencies = tracksDependencies
+ }
+
+ func updateBody(of container: V, changed: Bool) {
+ let (environment, environmentChanged) = $environment.changedValue()
+ guard changed ||
+ (
+ environmentChanged && (
+ !tracksDependencies ||
+ tracker.hasDifferentUsedValues(environment.plist)
+ )
+ )
+ else {
+ return
+ }
+ tracker.reset()
+ let newEnvironment = EnvironmentValues(environment.plist, tracker: tracker)
+ setBody {
+ container.resolve(in: newEnvironment)
+ }
+ }
+}
diff --git a/Sources/OpenSwiftUICore/View/CustomView.swift b/Sources/OpenSwiftUICore/View/CustomView.swift
index 132509472..7ee20a4bf 100644
--- a/Sources/OpenSwiftUICore/View/CustomView.swift
+++ b/Sources/OpenSwiftUICore/View/CustomView.swift
@@ -51,14 +51,11 @@ extension View {
inputs: inout _GraphInputs,
fields: DynamicPropertyCache.Fields
) -> (_GraphValue, _DynamicPropertyBuffer?) {
- let kind = Metadata(Self.self).kind
- switch kind {
- case .struct, .enum, .optional, .tuple:
- let accessor = ViewBodyAccessor()
- return accessor.makeBody(container: view, inputs: &inputs, fields: fields)
- default:
+ guard Metadata(Self.self).isValueType else {
preconditionFailure("views must be value types (either a struct or an enum); \(Self.self) is a class.")
}
+ let accessor = ViewBodyAccessor()
+ return accessor.makeBody(container: view, inputs: &inputs, fields: fields)
}
}
diff --git a/Sources/OpenSwiftUICore/View/VariadicView.swift b/Sources/OpenSwiftUICore/View/VariadicView.swift
index e2487b51a..722ef39ff 100644
--- a/Sources/OpenSwiftUICore/View/VariadicView.swift
+++ b/Sources/OpenSwiftUICore/View/VariadicView.swift
@@ -348,14 +348,11 @@ extension _VariadicView.ViewRoot {
inputs: inout _GraphInputs,
fields: DynamicPropertyCache.Fields
) -> (_GraphValue, _DynamicPropertyBuffer?) {
- let kind = Metadata(Self.self).kind
- switch kind {
- case .struct, .enum, .optional, .tuple:
- let accessor = ViewRootBodyAccessor(list: list, contentSubgraph: .current!)
- return accessor.makeBody(container: root, inputs: &inputs, fields: fields)
- default:
+ guard Metadata(Self.self).isValueType else {
preconditionFailure("views root must be value types (either a struct or an enum); \(Self.self) is a class.")
}
+ let accessor = ViewRootBodyAccessor(list: list, contentSubgraph: .current!)
+ return accessor.makeBody(container: root, inputs: &inputs, fields: fields)
}
}