Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions Sources/OpenSwiftUICore/Modifier/CustomViewModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,11 @@ extension ViewModifier {
inputs: inout _GraphInputs,
fields: DynamicPropertyCache.Fields
) -> (_GraphValue<Body>, _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<Self>()
return accessor.makeBody(container: modifier, inputs: &inputs, fields: fields)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<Self>,
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<Self>,
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<Self>,
inputs: inout _GraphInputs,
fields: DynamicPropertyCache.Fields
) -> (_GraphValue<ResolvedModifier>, _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<Self>(
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<V>: 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<EnvironmentValues>,
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)
}
}
}
9 changes: 3 additions & 6 deletions Sources/OpenSwiftUICore/View/CustomView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,11 @@ extension View {
inputs: inout _GraphInputs,
fields: DynamicPropertyCache.Fields
) -> (_GraphValue<Body>, _DynamicPropertyBuffer?) {
let kind = Metadata(Self.self).kind
switch kind {
case .struct, .enum, .optional, .tuple:
let accessor = ViewBodyAccessor<Self>()
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<Self>()
return accessor.makeBody(container: view, inputs: &inputs, fields: fields)
}
}

Expand Down
9 changes: 3 additions & 6 deletions Sources/OpenSwiftUICore/View/VariadicView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -348,14 +348,11 @@ extension _VariadicView.ViewRoot {
inputs: inout _GraphInputs,
fields: DynamicPropertyCache.Fields
) -> (_GraphValue<Body>, _DynamicPropertyBuffer?) {
let kind = Metadata(Self.self).kind
switch kind {
case .struct, .enum, .optional, .tuple:
let accessor = ViewRootBodyAccessor<Self>(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<Self>(list: list, contentSubgraph: .current!)
return accessor.makeBody(container: root, inputs: &inputs, fields: fields)
}
}

Expand Down
Loading