diff --git a/Sources/Model/Data Flow/Environment.swift b/Sources/Model/Data Flow/Environment.swift new file mode 100644 index 0000000..ad76621 --- /dev/null +++ b/Sources/Model/Data Flow/Environment.swift @@ -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: 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 } + +} diff --git a/Sources/Model/User Interface/View/View.swift b/Sources/Model/User Interface/View/View.swift index 4e7d0d2..281d3c7 100644 --- a/Sources/Model/User Interface/View/View.swift +++ b/Sources/Model/User Interface/View/View.swift @@ -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 + } + } diff --git a/Sources/View/DataWrapper.swift b/Sources/View/DataWrapper.swift new file mode 100644 index 0000000..077a640 --- /dev/null +++ b/Sources/View/DataWrapper.swift @@ -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: 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( + _ 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) + } + +} diff --git a/Sources/View/StateWrapper.swift b/Sources/View/StateWrapper.swift index 0991eb8..426e907 100644 --- a/Sources/View/StateWrapper.swift +++ b/Sources/View/StateWrapper.swift @@ -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] + } + } + } diff --git a/Tests/DemoApp/DemoApp.swift b/Tests/DemoApp/DemoApp.swift index c18c87b..cf6960f 100644 --- a/Tests/DemoApp/DemoApp.swift +++ b/Tests/DemoApp/DemoApp.swift @@ -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