Add support for environment properties
This commit is contained in:
parent
a8ce63a67f
commit
ee92f63f86
49
Sources/Model/Data Flow/Environment.swift
Normal file
49
Sources/Model/Data Flow/Environment.swift
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// Environment.swift
|
||||
// Meta
|
||||
//
|
||||
// Created by david-swift on 23.10.24.
|
||||
//
|
||||
|
||||
/// A property wrapper for properties in a view that should be stored throughout view updates.
|
||||
@propertyWrapper
|
||||
public struct Environment<Value>: EnvironmentProtocol {
|
||||
|
||||
/// Access the environment value.
|
||||
public var wrappedValue: Value? {
|
||||
content.value as? Value
|
||||
}
|
||||
/// The value's identifier.
|
||||
var id: String
|
||||
/// The content.
|
||||
let content = EnvironmentContent()
|
||||
|
||||
// swiftlint:disable function_default_parameter_at_end
|
||||
/// Initialize a property representing an environment value in the view.
|
||||
/// - Parameters:
|
||||
/// - wrappedValue: The wrapped value.
|
||||
/// - id: The environment value's identifier.
|
||||
public init(wrappedValue: Value? = nil, _ id: String) {
|
||||
self.id = id
|
||||
}
|
||||
// swiftlint:enable function_default_parameter_at_end
|
||||
|
||||
}
|
||||
|
||||
/// An environment property's content.
|
||||
class EnvironmentContent {
|
||||
|
||||
/// The value.
|
||||
var value: Any?
|
||||
|
||||
}
|
||||
|
||||
/// The environment property protocol.
|
||||
protocol EnvironmentProtocol {
|
||||
|
||||
/// The content.
|
||||
var content: EnvironmentContent { get }
|
||||
/// The identifier.
|
||||
var id: String { get }
|
||||
|
||||
}
|
||||
@ -32,7 +32,7 @@ extension View {
|
||||
|
||||
/// The view's content.
|
||||
public var viewContent: Body {
|
||||
[StateWrapper(content: { view }, state: getState())]
|
||||
[StateWrapper(content: { view }, state: getState(), environment: getEnvironmentVariables())]
|
||||
}
|
||||
|
||||
/// Get the state from the properties.
|
||||
@ -47,4 +47,16 @@ extension View {
|
||||
return state
|
||||
}
|
||||
|
||||
/// Get the environment properties.
|
||||
/// - Returns: The environment properties.
|
||||
func getEnvironmentVariables() -> [String: any EnvironmentProtocol] {
|
||||
var environment: [String: any EnvironmentProtocol] = [:]
|
||||
for property in Mirror(reflecting: self).children {
|
||||
if let label = property.label, let value = property.value as? any EnvironmentProtocol {
|
||||
environment[label] = value
|
||||
}
|
||||
}
|
||||
return environment
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
67
Sources/View/DataWrapper.swift
Normal file
67
Sources/View/DataWrapper.swift
Normal file
@ -0,0 +1,67 @@
|
||||
//
|
||||
// StateWrapper.swift
|
||||
// Meta
|
||||
//
|
||||
// Created by david-swift on 09.06.24.
|
||||
//
|
||||
|
||||
/// Assign values to the environment.
|
||||
///
|
||||
/// Access the environment in views (``View``) via `@Environment`.
|
||||
struct DataWrapper: ConvenienceWidget {
|
||||
|
||||
/// The content.
|
||||
var content: Body
|
||||
/// The identifier for the new environment value.
|
||||
var label: String
|
||||
/// The environment value.
|
||||
var data: Any
|
||||
|
||||
/// Get a view storage.
|
||||
/// - Parameters:
|
||||
/// - data: Modify views before being updated.
|
||||
/// - type: The view render data type.
|
||||
/// - Returns: The view storage.
|
||||
func container<Data>(
|
||||
data: WidgetData,
|
||||
type: Data.Type
|
||||
) -> ViewStorage where Data: ViewRenderData {
|
||||
content.storage(data: data.modify { $0.fields[label] = self.data }, type: type)
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
/// - Parameters:
|
||||
/// - storage: The view storage.
|
||||
/// - data: Modify views before being updated.
|
||||
/// - updateProperties: Whether to update properties.
|
||||
/// - type: The view render data type.
|
||||
/// - Returns: The view storage.
|
||||
func update<Data>(
|
||||
_ storage: ViewStorage,
|
||||
data: WidgetData,
|
||||
updateProperties: Bool,
|
||||
type: Data.Type
|
||||
) where Data: ViewRenderData {
|
||||
content
|
||||
.updateStorage(
|
||||
storage,
|
||||
data: data.modify { $0.fields[label] = self.data },
|
||||
updateProperties: updateProperties,
|
||||
type: type
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AnyView {
|
||||
|
||||
/// Assign a value to an environment label.
|
||||
/// - Parameters:
|
||||
/// - label: The environment label.
|
||||
/// - data: The value.
|
||||
/// - Returns: The view.
|
||||
public func environment(_ label: String, data: Any) -> AnyView {
|
||||
DataWrapper(content: [self], label: label, data: data)
|
||||
}
|
||||
|
||||
}
|
||||
@ -12,6 +12,8 @@ struct StateWrapper: ConvenienceWidget {
|
||||
var content: () -> Body
|
||||
/// The state information (from properties with the `State` wrapper).
|
||||
var state: [String: StateProtocol] = [:]
|
||||
/// The environment properties.
|
||||
var environment: [String: any EnvironmentProtocol] = [:]
|
||||
|
||||
/// Initialize a `StateWrapper`.
|
||||
/// - Parameter content: The view content.
|
||||
@ -23,9 +25,15 @@ struct StateWrapper: ConvenienceWidget {
|
||||
/// - Parameters:
|
||||
/// - content: The view content.
|
||||
/// - state: The state information.
|
||||
init(content: @escaping () -> Body, state: [String: StateProtocol]) {
|
||||
/// - environment: The environment properties.
|
||||
init(
|
||||
content: @escaping () -> Body,
|
||||
state: [String: StateProtocol],
|
||||
environment: [String: any EnvironmentProtocol]
|
||||
) {
|
||||
self.content = content
|
||||
self.state = state
|
||||
self.environment = environment
|
||||
}
|
||||
|
||||
/// Update a view storage.
|
||||
@ -51,6 +59,7 @@ struct StateWrapper: ConvenienceWidget {
|
||||
property.value.content.update = false
|
||||
}
|
||||
}
|
||||
assignEnvironment(data: data)
|
||||
guard let storage = storage.content[.mainContent]?.first else {
|
||||
return
|
||||
}
|
||||
@ -66,6 +75,7 @@ struct StateWrapper: ConvenienceWidget {
|
||||
data: WidgetData,
|
||||
type: Data.Type
|
||||
) -> ViewStorage where Data: ViewRenderData {
|
||||
assignEnvironment(data: data)
|
||||
let content = content().storage(data: data, type: type)
|
||||
let storage = ViewStorage(content.pointer, content: [.mainContent: [content]])
|
||||
storage.state = state
|
||||
@ -75,4 +85,12 @@ struct StateWrapper: ConvenienceWidget {
|
||||
return storage
|
||||
}
|
||||
|
||||
/// Assign an environment value to the environment property.
|
||||
/// - Parameter data: The widget data.
|
||||
func assignEnvironment(data: WidgetData) {
|
||||
for property in environment {
|
||||
property.value.content.value = data.fields[property.value.id]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ struct DemoApp: App {
|
||||
var scene: Scene {
|
||||
Backend1.Window("main", spawn: 1) {
|
||||
DemoView(app: app)
|
||||
.environment("test", data: 5)
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +32,8 @@ struct DemoApp: App {
|
||||
struct DemoView: View {
|
||||
|
||||
@State private var model = TestModel()
|
||||
@Environment("test")
|
||||
private var test: Int?
|
||||
var app: any AppStorage
|
||||
let condition = false
|
||||
|
||||
@ -46,6 +49,7 @@ struct DemoView: View {
|
||||
app.addSceneElement("main")
|
||||
}
|
||||
}
|
||||
.onAppear { print(test ?? 0) }
|
||||
}
|
||||
TestView()
|
||||
testContent
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user