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.
|
/// The view's content.
|
||||||
public var viewContent: Body {
|
public var viewContent: Body {
|
||||||
[StateWrapper(content: { view }, state: getState())]
|
[StateWrapper(content: { view }, state: getState(), environment: getEnvironmentVariables())]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the state from the properties.
|
/// Get the state from the properties.
|
||||||
@ -47,4 +47,16 @@ extension View {
|
|||||||
return state
|
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
|
var content: () -> Body
|
||||||
/// The state information (from properties with the `State` wrapper).
|
/// The state information (from properties with the `State` wrapper).
|
||||||
var state: [String: StateProtocol] = [:]
|
var state: [String: StateProtocol] = [:]
|
||||||
|
/// The environment properties.
|
||||||
|
var environment: [String: any EnvironmentProtocol] = [:]
|
||||||
|
|
||||||
/// Initialize a `StateWrapper`.
|
/// Initialize a `StateWrapper`.
|
||||||
/// - Parameter content: The view content.
|
/// - Parameter content: The view content.
|
||||||
@ -23,9 +25,15 @@ struct StateWrapper: ConvenienceWidget {
|
|||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - content: The view content.
|
/// - content: The view content.
|
||||||
/// - state: The state information.
|
/// - 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.content = content
|
||||||
self.state = state
|
self.state = state
|
||||||
|
self.environment = environment
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update a view storage.
|
/// Update a view storage.
|
||||||
@ -51,6 +59,7 @@ struct StateWrapper: ConvenienceWidget {
|
|||||||
property.value.content.update = false
|
property.value.content.update = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assignEnvironment(data: data)
|
||||||
guard let storage = storage.content[.mainContent]?.first else {
|
guard let storage = storage.content[.mainContent]?.first else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -66,6 +75,7 @@ struct StateWrapper: ConvenienceWidget {
|
|||||||
data: WidgetData,
|
data: WidgetData,
|
||||||
type: Data.Type
|
type: Data.Type
|
||||||
) -> ViewStorage where Data: ViewRenderData {
|
) -> ViewStorage where Data: ViewRenderData {
|
||||||
|
assignEnvironment(data: data)
|
||||||
let content = content().storage(data: data, type: type)
|
let content = content().storage(data: data, type: type)
|
||||||
let storage = ViewStorage(content.pointer, content: [.mainContent: [content]])
|
let storage = ViewStorage(content.pointer, content: [.mainContent: [content]])
|
||||||
storage.state = state
|
storage.state = state
|
||||||
@ -75,4 +85,12 @@ struct StateWrapper: ConvenienceWidget {
|
|||||||
return storage
|
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 {
|
var scene: Scene {
|
||||||
Backend1.Window("main", spawn: 1) {
|
Backend1.Window("main", spawn: 1) {
|
||||||
DemoView(app: app)
|
DemoView(app: app)
|
||||||
|
.environment("test", data: 5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +32,8 @@ struct DemoApp: App {
|
|||||||
struct DemoView: View {
|
struct DemoView: View {
|
||||||
|
|
||||||
@State private var model = TestModel()
|
@State private var model = TestModel()
|
||||||
|
@Environment("test")
|
||||||
|
private var test: Int?
|
||||||
var app: any AppStorage
|
var app: any AppStorage
|
||||||
let condition = false
|
let condition = false
|
||||||
|
|
||||||
@ -46,6 +49,7 @@ struct DemoView: View {
|
|||||||
app.addSceneElement("main")
|
app.addSceneElement("main")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onAppear { print(test ?? 0) }
|
||||||
}
|
}
|
||||||
TestView()
|
TestView()
|
||||||
testContent
|
testContent
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user