From 77b29768c0c22ba746e0779a5663da0be2aa09ac Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 7 Sep 2025 16:27:25 +0800 Subject: [PATCH 1/2] Update isValueType usage --- .../OpenSwiftUICore/Modifier/CustomViewModifier.swift | 8 +++----- Sources/OpenSwiftUICore/View/CustomView.swift | 9 +++------ Sources/OpenSwiftUICore/View/VariadicView.swift | 9 +++------ 3 files changed, 9 insertions(+), 17 deletions(-) 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/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) } } From e9cb1c95857ca1d0a2b68f204e72db96c6ce4513 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 7 Sep 2025 17:44:52 +0800 Subject: [PATCH 2/2] Add EnvironmentalModifier support --- .../ViewModifier/EnvironmentalModifier.swift | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 Sources/OpenSwiftUICore/Modifier/ViewModifier/EnvironmentalModifier.swift 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) + } + } +}