diff --git a/Sources/Model/Extensions/Array.swift b/Sources/Model/Extensions/Array.swift index 62813d5..7033179 100644 --- a/Sources/Model/Extensions/Array.swift +++ b/Sources/Model/Extensions/Array.swift @@ -26,9 +26,7 @@ extension Array: AnyView where Element == AnyView { } else { var modified = self for (index, view) in modified.enumerated() { - for modifier in modifiers { - modified[safe: index] = modifier(view) - } + modified[safe: index] = view.getModified(modifiers: modifiers, type: type) } return Data.WrapperType { modified } } diff --git a/Sources/Model/User Interface/View/AnyView.swift b/Sources/Model/User Interface/View/AnyView.swift index 4493215..eddd5ea 100644 --- a/Sources/Model/User Interface/View/AnyView.swift +++ b/Sources/Model/User Interface/View/AnyView.swift @@ -15,11 +15,14 @@ public protocol AnyView { extension AnyView { - func getModified(modifiers: [(AnyView) -> AnyView]) -> AnyView { + func getModified(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> AnyView where Data: ViewRenderData { var modified: AnyView = self for modifier in modifiers { modified = modifier(modified) } + if let dummy = modified as? DummyEitherView { + modified = type.EitherViewType(dummy.condition) { dummy.view1 ?? [] } else: { dummy.view2 ?? [] } + } return modified } @@ -55,19 +58,19 @@ extension AnyView { /// - Parameter modifiers: Modify views before being updated. /// - Returns: The widget. func widget(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> Widget where Data: ViewRenderData { - let modified = getModified(modifiers: modifiers) + let modified = getModified(modifiers: modifiers, type: type) if let peer = modified as? Widget { return peer } if let array = modified as? Body { return Data.WrapperType { array } } - return Data.WrapperType { viewContent.map { $0.getModified(modifiers: modifiers) } } + return Data.WrapperType { viewContent.map { $0.getModified(modifiers: modifiers, type: type) } } } /// Whether the view can be rendered in a certain environment. func renderable(type: Data.Type, modifiers: [(AnyView) -> AnyView]) -> Bool where Data: ViewRenderData { - let result = getModified(modifiers: modifiers) + let result = getModified(modifiers: modifiers, type: type) return result as? Data.WidgetType != nil || result as? SimpleView != nil || result as? View != nil diff --git a/Sources/Model/User Interface/View/EitherView.swift b/Sources/Model/User Interface/View/EitherView.swift new file mode 100644 index 0000000..571a8e0 --- /dev/null +++ b/Sources/Model/User Interface/View/EitherView.swift @@ -0,0 +1,22 @@ +// +// EitherView.swift +// Meta +// +// Created by david-swift on 06.08.24. +// + +/// A view building conditional bodies. +public protocol EitherView: AnyView { + + /// Initialize the either view. + /// - Parameters: + /// - condition: Whether the first view is visible- + /// - view1: The first view, visible if true. + /// - view2: The second view, visible if false. + init( + _ condition: Bool, + @ViewBuilder view1: () -> Body, + @ViewBuilder else view2: () -> Body + ) + +} diff --git a/Sources/Model/User Interface/View/ViewBuilder.swift b/Sources/Model/User Interface/View/ViewBuilder.swift index 4fd1af9..45f7bd8 100644 --- a/Sources/Model/User Interface/View/ViewBuilder.swift +++ b/Sources/Model/User Interface/View/ViewBuilder.swift @@ -53,6 +53,27 @@ public enum ViewBuilder { component } + /// Enables support for `if` statements without an `else`. + /// - Parameter component: An optional component. + /// - Returns: A nonoptional component. + public static func buildOptional(_ component: Component?) -> Component { + .element(DummyEitherView(condition: component != nil, view1: buildFinalResult(component ?? .element([])))) + } + + /// Enables support for `if`-`else` and `switch` statements. + /// - Parameter component: A component. + /// - Returns: The component. + public static func buildEither(first component: Component) -> Component { + .element(DummyEitherView(condition: true, view1: buildFinalResult(component))) + } + + /// Enables support for `if`-`else` and `switch` statements. + /// - Parameter component: A component. + /// - Returns: The component. + public static func buildEither(second component: Component) -> Component { + .element(DummyEitherView(condition: false, view2: buildFinalResult(component))) + } + /// Convert a component to an array of elements. /// - Parameter component: The component to convert. /// - Returns: The generated array of elements. diff --git a/Sources/Model/User Interface/View/ViewRenderData.swift b/Sources/Model/User Interface/View/ViewRenderData.swift index 9458e0a..091fa22 100644 --- a/Sources/Model/User Interface/View/ViewRenderData.swift +++ b/Sources/Model/User Interface/View/ViewRenderData.swift @@ -12,5 +12,7 @@ public protocol ViewRenderData { associatedtype WidgetType /// The wrapper widget. associatedtype WrapperType: Wrapper + /// The type replacing dummy either views. + associatedtype EitherViewType: EitherView } diff --git a/Sources/View/DummyEitherView.swift b/Sources/View/DummyEitherView.swift new file mode 100644 index 0000000..6a1e83b --- /dev/null +++ b/Sources/View/DummyEitherView.swift @@ -0,0 +1,21 @@ +// +// DummyEitherView.swift +// Meta +// +// Created by david-swift on 06.08.24. +// + +/// A dummy either view. This will be replaced by the platform-specific either view. +struct DummyEitherView: SimpleView { + + /// Whether to present the first view. + var condition: Bool + /// The first view. + var view1: Body? + /// The second view. + var view2: Body? + + /// By default, show the first view. + var view: Body { view1 ?? view2 ?? [] } + +} diff --git a/Tests/DemoApp/DemoApp.swift b/Tests/DemoApp/DemoApp.swift index d07ce77..3df2f03 100644 --- a/Tests/DemoApp/DemoApp.swift +++ b/Tests/DemoApp/DemoApp.swift @@ -33,9 +33,14 @@ struct DemoView: View { @State private var model = TestModel() var app: any AppStorage + let condition = false var view: Body { - Backend1.TestWidget1() + if condition { + Backend1.TestWidget1() + } else { + Backend1.TestWidget3() + } Backend1.Button(model.test) { Task { app.addSceneElement("main") diff --git a/Tests/SampleBackends/Backend1.swift b/Tests/SampleBackends/Backend1.swift index 4f935db..f66cfe2 100644 --- a/Tests/SampleBackends/Backend1.swift +++ b/Tests/SampleBackends/Backend1.swift @@ -131,10 +131,25 @@ public enum Backend1 { } + public struct EitherView: BackendWidget, Meta.EitherView { + + public init(_ condition: Bool, view1: () -> Body, else view2: () -> Body) { + } + + public func container(modifiers: [(any AnyView) -> any AnyView], type: Data.Type) -> ViewStorage where Data : ViewRenderData { + .init(nil) + } + + public func update(_ storage: ViewStorage, modifiers: [(any AnyView) -> any AnyView], updateProperties: Bool, type: Data.Type) where Data : ViewRenderData { + } + + } + public struct MainViewRenderData: ViewRenderData { public typealias WidgetType = BackendWidget public typealias WrapperType = Wrapper + public typealias EitherViewType = EitherView }