diff --git a/Demo/JudoDemo.xcodeproj/project.pbxproj b/Demo/JudoDemo.xcodeproj/project.pbxproj index 6cca47f..b158551 100644 --- a/Demo/JudoDemo.xcodeproj/project.pbxproj +++ b/Demo/JudoDemo.xcodeproj/project.pbxproj @@ -299,7 +299,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 7.0; + MARKETING_VERSION = 7.0.3; PRODUCT_BUNDLE_IDENTIFIER = app.judo.Demo; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -332,7 +332,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 7.0; + MARKETING_VERSION = 7.0.3; PRODUCT_BUNDLE_IDENTIFIER = app.judo.Demo; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/Sources/JudoDocument/Nodes/DocumentNode.swift b/Sources/JudoDocument/Nodes/DocumentNode.swift index 0cbc0f3..83207a2 100644 --- a/Sources/JudoDocument/Nodes/DocumentNode.swift +++ b/Sources/JudoDocument/Nodes/DocumentNode.swift @@ -60,7 +60,7 @@ public struct DocumentNode: Node { self.userData = userData } - public var allNodes: [Any] { + public var allNodes: [Node] { children + deletedNodes } diff --git a/Sources/JudoDocument/Value Types/Expression.swift b/Sources/JudoDocument/Value Types/Expression.swift index 50fb098..eedfc78 100644 --- a/Sources/JudoDocument/Value Types/Expression.swift +++ b/Sources/JudoDocument/Value Types/Expression.swift @@ -36,15 +36,15 @@ public struct Expression: Hasha } /// Evaluates an expression using a specified properties and functions. - func evaluate(propertyValues: [String: PropertyValue], data: Any?, functions: [ExpressionFunction] = []) throws -> T? { + func evaluate(propertyValues: [String: PropertyValue], data: Any?, functions: [JudoExpressionFunction] = []) throws -> T? { var variables = propertyValues.compactMap { element in switch element.value { case .number(let doubleValue): - return ExpressionVariable(element.key, value: doubleValue) + return JudoExpressionVariable(element.key, value: doubleValue) case .text(let stringValue): - return ExpressionVariable(element.key, value: stringValue) + return JudoExpressionVariable(element.key, value: stringValue) case .boolean(let boolValue): - return ExpressionVariable(element.key, value: boolValue) + return JudoExpressionVariable(element.key, value: boolValue) case .computed(let computedValue): // replace expression with resulting value @@ -53,12 +53,12 @@ public struct Expression: Hasha switch computedValue { case .number(let expression): - return try? ExpressionVariable(element.key, value: expression.evaluate(propertyValues: propertyValues, data: data, functions: functions)) + return try? JudoExpressionVariable(element.key, value: expression.evaluate(propertyValues: propertyValues, data: data, functions: functions)) case .text(let expression): - return try? ExpressionVariable(element.key, value: expression.evaluate(propertyValues: propertyValues, data: data, functions: functions)) + return try? JudoExpressionVariable(element.key, value: expression.evaluate(propertyValues: propertyValues, data: data, functions: functions)) case .boolean(let expression): if let evaluatedValue = try? expression.evaluate(propertyValues: propertyValues, data: data, functions: functions) { - return ExpressionVariable(element.key, value: evaluatedValue) + return JudoExpressionVariable(element.key, value: evaluatedValue) } else { return nil } @@ -76,18 +76,18 @@ public struct Expression: Hasha } /// Evaluates an expression using a specified variables and functions. - func evaluate(variables: [JudoExpressions.ExpressionVariable] = [], functions: [ExpressionFunction] = []) throws -> T? { - try Interpreter(variables: variables, functions: functions).interpret(expression) as? T + func evaluate(variables: [JudoExpressions.JudoExpressionVariable] = [], functions: [JudoExpressionFunction] = []) throws -> T? { + try JudoInterpreter(variables: variables, functions: functions).interpret(expression) as? T } - private func dataVariables(value: Any, keyPath: [String]) -> [ExpressionVariable]? { + private func dataVariables(value: Any, keyPath: [String]) -> [JudoExpressionVariable]? { switch value { case let number as Double: - return [ExpressionVariable(keyPath.joined(separator: "\u{B7}"), value: number)] + return [JudoExpressionVariable(keyPath.joined(separator: "\u{B7}"), value: number)] case let string as String: - return [ExpressionVariable(keyPath.joined(separator: "\u{B7}"), value: string)] + return [JudoExpressionVariable(keyPath.joined(separator: "\u{B7}"), value: string)] case let dictionary as [String: Any]: - return dictionary.reduce(into: [ExpressionVariable]()) { partialResult, element in + return dictionary.reduce(into: [JudoExpressionVariable]()) { partialResult, element in let keyPath = keyPath + [element.key] partialResult += dataVariables(value: element.value, keyPath: keyPath) ?? [] } @@ -149,7 +149,7 @@ extension Expression: ExpressibleByBooleanLiteral { /// Library of standard functions private let standardLibrary = [ - ExpressionFunction("formatted", closure: { caller, _ in + JudoExpressionFunction("formatted", closure: { caller, _ in guard let value = caller as? Double else { return caller } diff --git a/Sources/JudoExpressions/Interpreter/Callable.swift b/Sources/JudoExpressions/Interpreter/Callable.swift index 6f0f359..c459c70 100644 --- a/Sources/JudoExpressions/Interpreter/Callable.swift +++ b/Sources/JudoExpressions/Interpreter/Callable.swift @@ -14,5 +14,5 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. protocol Callable { - func call(_ interpreter: Interpreter, caller: Any?, _ args: [Any?]) throws -> Any? + func call(_ interpreter: JudoInterpreter, caller: Any?, _ args: [Any?]) throws -> Any? } diff --git a/Sources/JudoExpressions/Interpreter/ExpressionFunction.swift b/Sources/JudoExpressions/Interpreter/JudoExpressionFunction.swift similarity index 89% rename from Sources/JudoExpressions/Interpreter/ExpressionFunction.swift rename to Sources/JudoExpressions/Interpreter/JudoExpressionFunction.swift index b9ad8f5..168aaa4 100644 --- a/Sources/JudoExpressions/Interpreter/ExpressionFunction.swift +++ b/Sources/JudoExpressions/Interpreter/JudoExpressionFunction.swift @@ -13,7 +13,7 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -public struct ExpressionFunction: Callable { +public struct JudoExpressionFunction: Callable { let selector: String let closure: (_ caller: Any?, _ arguments: [Any?]) throws -> Any? @@ -22,7 +22,7 @@ public struct ExpressionFunction: Callable { self.closure = closure } - func call(_ interpreter: Interpreter, caller: Any?, _ args: [Any?]) throws -> Any? { + func call(_ interpreter: JudoInterpreter, caller: Any?, _ args: [Any?]) throws -> Any? { try closure(caller, args) } } diff --git a/Sources/JudoExpressions/Interpreter/ExpressionVariable.swift b/Sources/JudoExpressions/Interpreter/JudoExpressionVariable.swift similarity index 97% rename from Sources/JudoExpressions/Interpreter/ExpressionVariable.swift rename to Sources/JudoExpressions/Interpreter/JudoExpressionVariable.swift index 96d0c1d..da7c9c8 100644 --- a/Sources/JudoExpressions/Interpreter/ExpressionVariable.swift +++ b/Sources/JudoExpressions/Interpreter/JudoExpressionVariable.swift @@ -13,7 +13,7 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -public struct ExpressionVariable { +public struct JudoExpressionVariable { public let identifier: String let value: Any? diff --git a/Sources/JudoExpressions/Interpreter/Interpreter.swift b/Sources/JudoExpressions/Interpreter/JudoInterpreter.swift similarity index 95% rename from Sources/JudoExpressions/Interpreter/Interpreter.swift rename to Sources/JudoExpressions/Interpreter/JudoInterpreter.swift index ac22d1a..d133233 100644 --- a/Sources/JudoExpressions/Interpreter/Interpreter.swift +++ b/Sources/JudoExpressions/Interpreter/JudoInterpreter.swift @@ -25,13 +25,13 @@ private var numberFormatter: NumberFormatter = { return formatter }() -public class Interpreter: ExpressionVisitor, StatementVisitor { +public class JudoInterpreter: ExpressionVisitor, StatementVisitor { /// global variables - private let globalVariables: [ExpressionVariable] - private let globalFunctions: [ExpressionFunction] + private let globalVariables: [JudoExpressionVariable] + private let globalFunctions: [JudoExpressionFunction] - public init(variables: [ExpressionVariable] = [], functions: [ExpressionFunction] = []) { + public init(variables: [JudoExpressionVariable] = [], functions: [JudoExpressionFunction] = []) { self.globalVariables = variables self.globalFunctions = standardLibrary + functions } @@ -88,7 +88,7 @@ public class Interpreter: ExpressionVisitor, StatementVisitor { /// Call function with callee instance as caller /// Wrap original function in a function (method) that /// set instance (caller) as a caller - return ExpressionFunction(methodName) { _, arguments in + return JudoExpressionFunction(methodName) { _, arguments in try function.call(self, caller: caller, arguments) } } @@ -276,13 +276,13 @@ private extension Equatable { } } -private extension Array { +private extension Array { subscript(_ identifier: S) -> Any? { first(where: { $0.identifier == identifier })?.value } } -private extension Array { +private extension Array { subscript(_ identifier: S) -> Element? { first(where: { $0.selector == identifier }) } diff --git a/Sources/JudoExpressions/Interpreter/StdLib/StandardLibrary.swift b/Sources/JudoExpressions/Interpreter/StdLib/StandardLibrary.swift index 00721de..da6c776 100644 --- a/Sources/JudoExpressions/Interpreter/StdLib/StandardLibrary.swift +++ b/Sources/JudoExpressions/Interpreter/StdLib/StandardLibrary.swift @@ -17,7 +17,7 @@ import Foundation /// Library of standard functions let standardLibrary = [ - ExpressionFunction("formatted", closure: { caller, _ in + JudoExpressionFunction("formatted", closure: { caller, _ in guard let value = caller as? Double else { return caller } diff --git a/Sources/JudoRenderer/Environment/ComponentBindings.swift b/Sources/JudoRenderer/Environment/ComponentBindings.swift new file mode 100644 index 0000000..158f80f --- /dev/null +++ b/Sources/JudoRenderer/Environment/ComponentBindings.swift @@ -0,0 +1,48 @@ +// Copyright (c) 2023-present, Rover Labs, Inc. All rights reserved. +// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +// copy, modify, and distribute this software in source code or binary form for use +// in connection with the web services and APIs provided by Rover. +// +// This copyright notice shall be included in all copies or substantial portions of +// the software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import JudoDocument +import SwiftUI + +typealias ComponentBindings = [String: Binding] + +extension ComponentBindings { + var propertyValues: [String: PropertyValue] { + reduce(into: [:]) { partialResult, element in + switch element.value.wrappedValue { + case let value as String: + partialResult[element.key] = .text(value) + case let value as Double: + partialResult[element.key] = .number(value) + case let value as Bool: + partialResult[element.key] = .boolean(value) + case let value as ImageReference: + partialResult[element.key] = .image(value) + case let value as UUID: + partialResult[element.key] = .component(value) + case let value as Video: + partialResult[element.key] = .video(value) + case let value as Expression: + partialResult[element.key] = .computed(.text(value)) + case let value as Expression: + partialResult[element.key] = .computed(.number(value)) + case let value as Expression: + partialResult[element.key] = .computed(.boolean(value)) + default: + break + } + } + } +} diff --git a/Sources/JudoRenderer/Environment/ComponentState.swift b/Sources/JudoRenderer/Environment/ComponentState.swift deleted file mode 100644 index 0f650b4..0000000 --- a/Sources/JudoRenderer/Environment/ComponentState.swift +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) 2023-present, Rover Labs, Inc. All rights reserved. -// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, -// copy, modify, and distribute this software in source code or binary form for use -// in connection with the web services and APIs provided by Rover. -// -// This copyright notice shall be included in all copies or substantial portions of -// the software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import SwiftUI -import JudoDocument - -final class ComponentState: ObservableObject { - @Published var bindings: [String: ComponentBinding] - @Published var views: [String: any View] - - var propertyValues: [String: PropertyValue] { - bindings.reduce(into: [String: PropertyValue]()) { partialResult, element in - partialResult[element.key] = element.value.value - } - } - - init(propertyValues: [String: PropertyValue], overrides: Overrides, data: Any?, fetchedImage: SwiftUI.Image?, parentState: ComponentState?) { - var resultingBindings = propertyValues.mapValues { - ComponentBinding($0) - } - - var resultingViews: [String: any View] = [:] - - for (key, value) in propertyValues { - switch (value, overrides[key]) { - case (.text, .text(let textValue)): - if case .property(let propertyName) = textValue.binding, let parentState, parentState.bindings.keys.contains(propertyName) { - resultingBindings[key] = ComponentBinding(Binding { - let binding = parentState.bindings[propertyName]! - switch binding.value { - case .computed(let computedValue): - switch computedValue { - case .text(let expression): - if let resolvedValue = expression.resolve(propertyValues: parentState.propertyValues, data: data) { - return .text(resolvedValue) - } - case .number(let expression): - if let resolvedValue = expression.resolve(propertyValues: parentState.propertyValues, data: data) { - return .number(resolvedValue) - } - case .boolean(let expression): - if let resolvedValue = expression.resolve(propertyValues: parentState.propertyValues, data: data) { - return .boolean(resolvedValue) - } - } - return binding.value - default: - return binding.value - } - } set: { newValue in - parentState.bindings[propertyName]!.value = newValue - }) - } else { - let resolvedValue = textValue.forceResolve( - propertyValues: propertyValues, - data: data - ) - - resultingBindings[key] = ComponentBinding(.text(resolvedValue)) - } - case (.number(let defaultValue), .number(let numberValue)): - if case .property(let propertyName) = numberValue.binding, let parentState, parentState.bindings.keys.contains(propertyName) { - resultingBindings[key] = ComponentBinding(Binding { - let binding = parentState.bindings[propertyName]! - switch binding.value { - case .computed(let computedValue): - switch computedValue { - case .text(let expression): - if let resolvedValue = expression.resolve(propertyValues: parentState.propertyValues, data: data) { - return .text(resolvedValue) - } - case .number(let expression): - if let resolvedValue = expression.resolve(propertyValues: parentState.propertyValues, data: data) { - return .number(resolvedValue) - } - case .boolean(let expression): - if let resolvedValue = expression.resolve(propertyValues: parentState.propertyValues, data: data) { - return .boolean(resolvedValue) - } - } - return binding.value - default: - return binding.value - } - } set: { newValue in - parentState.bindings[propertyName]!.value = newValue - }) - } else { - let resolvedValue = numberValue.resolve( - propertyValues: propertyValues, - data: data - ) - resultingBindings[key] = ComponentBinding(.number(resolvedValue ?? defaultValue)) - } - case (.boolean, .boolean(let booleanValue)): - if case .property(let propertyName) = booleanValue.binding, let parentState, parentState.bindings.keys.contains(propertyName) { - resultingBindings[key] = ComponentBinding(Binding { - let binding = parentState.bindings[propertyName]! - switch binding.value { - case .computed(let computedValue): - switch computedValue { - case .text(let expression): - if let resolvedValue = expression.resolve(propertyValues: parentState.propertyValues, data: data) { - return .text(resolvedValue) - } - case .number(let expression): - if let resolvedValue = expression.resolve(propertyValues: parentState.propertyValues, data: data) { - return .number(resolvedValue) - } - case .boolean(let expression): - if let resolvedValue = expression.resolve(propertyValues: parentState.propertyValues, data: data) { - return .boolean(resolvedValue) - } - } - return binding.value - default: - return binding.value - } - } set: { newValue in - parentState.bindings[propertyName]!.value = newValue - }) - } else { - let resolvedValue = booleanValue.forceResolve( - propertyValues: propertyValues, - data: data - ) - resultingBindings[key] = ComponentBinding(.boolean(resolvedValue)) - } - case (.image, .image(let value)): - if case .property(let propertyName) = value.binding, let parentState, parentState.bindings.keys.contains(propertyName) { - resultingBindings[key] = ComponentBinding(Binding { - parentState.bindings[propertyName]!.value - } set: { newValue in - parentState.bindings[propertyName]!.value = newValue - }) - } else { - let resolvedValue = value.forceResolve( - propertyValues: propertyValues, - data: data, - fetchedImage: fetchedImage - ) - - resultingBindings[key] = ComponentBinding(.image(resolvedValue)) - } - case (.component(let defaultValue), .component(let value)): - if case .property(let propertyName) = value.binding, let parentState, parentState.bindings.keys.contains(propertyName) { - resultingBindings[key] = ComponentBinding(Binding { - parentState.bindings[propertyName]!.value - } set: { newValue in - parentState.bindings[propertyName]!.value = newValue - }) - } else { - let resolvedValue = value.resolve( - propertyValues: propertyValues, - data: data - ) - - resultingBindings[key] = ComponentBinding(.component(resolvedValue ?? defaultValue)) - } - - resultingViews[key] = parentState?.views[key] - case (.video, .video(let videoValue)): - if case .property(let propertyName) = videoValue.binding, let parentState, parentState.bindings.keys.contains(propertyName) { - resultingBindings[key] = ComponentBinding(Binding { - parentState.bindings[propertyName]!.value - } set: { newValue in - parentState.bindings[propertyName]!.value = newValue - }) - } else { - let resolvedValue = videoValue.forceResolve( - propertyValues: propertyValues, - data: data - ) - - resultingBindings[key] = ComponentBinding(.video(resolvedValue)) - } - default: - break - } - } - - self.bindings = resultingBindings - self.views = resultingViews - } - - init(propertyValues: [String: PropertyValue]) { - self.bindings = propertyValues.mapValues({ ComponentBinding($0) }) - self.views = [:] - } - - init(bindings: [String: ComponentBinding], views: [String: any View]) { - self.bindings = bindings - self.views = views - } -} - -struct ComponentBinding { - private var ownedValue: PropertyValue? - private var propertyBinding: Binding? - - var value: PropertyValue { - get { - if let ownedValue { - return ownedValue - } - - if let propertyBinding { - return propertyBinding.wrappedValue - } - - fatalError() - } - - set { - if let propertyBinding { - propertyBinding.wrappedValue = newValue - } else { - ownedValue = newValue - } - } - } - - init(_ value: PropertyValue) { - self.ownedValue = value - } - - init(_ propertyBinding: Binding) { - self.propertyBinding = propertyBinding - } -} diff --git a/Sources/JudoRenderer/Environment/EnvironmentValues.swift b/Sources/JudoRenderer/Environment/EnvironmentValues.swift index 90c4b18..25b4de7 100644 --- a/Sources/JudoRenderer/Environment/EnvironmentValues.swift +++ b/Sources/JudoRenderer/Environment/EnvironmentValues.swift @@ -36,6 +36,10 @@ private struct FetchedImageKey: EnvironmentKey { static var defaultValue: SwiftUI.Image? } +private struct ComponentBindingsKey: EnvironmentKey { + static var defaultValue = ComponentBindings() +} + extension EnvironmentValues { var assetManager: AssetManager { get { self[AssetManagerKey.self] } @@ -61,4 +65,9 @@ extension EnvironmentValues { get { self[FetchedImageKey.self] } set { self[FetchedImageKey.self] = newValue } } + + var componentBindings: ComponentBindings { + get { self[ComponentBindingsKey.self] } + set { self[ComponentBindingsKey.self] = newValue } + } } diff --git a/Sources/JudoRenderer/JudoView.swift b/Sources/JudoRenderer/JudoView.swift index 26e9596..8d0fbf0 100644 --- a/Sources/JudoRenderer/JudoView.swift +++ b/Sources/JudoRenderer/JudoView.swift @@ -92,31 +92,23 @@ public struct JudoView: View { public var body: some SwiftUI.View { switch result { case .success(let document): - if let startPoint = document.startPoint(preferring: componentName) { - switch startPoint { - case .artboard(let artboard): - ArtboardView( - artboard: artboard, - userBindings: userBindings, - userViews: userViews - ) - .id(userBindings.mapValues(\.value)) + switch document.startPoint(preferring: componentName) { + case .artboard(let artboard): + ArtboardView(artboard: artboard) .environment(\.document, document) .environment(\.assetManager, AssetManager(assets: document.assets)) .environment(\.actionHandlers, actionHandlers) - - case .component(let component): - MainComponentView( - component: component, - userBindings: userBindings, - userViews: userViews - ) - .id(userBindings.mapValues(\.value)) - .environment(\.document, document) - .environment(\.assetManager, AssetManager(assets: document.assets)) - .environment(\.actionHandlers, actionHandlers) - } - } else { + case .component(let mainComponent): + MainComponentView( + mainComponent: mainComponent, + userProperties: properties.reduce(into: [:]) { partialResult, element in + partialResult[element.key.rawValue] = element.value + } + ) + .environment(\.document, document) + .environment(\.assetManager, AssetManager(assets: document.assets)) + .environment(\.actionHandlers, actionHandlers) + case .none: JudoErrorView(.emptyFile) } case .failure(let error): @@ -124,85 +116,6 @@ public struct JudoView: View { } } - private var userViews: [String: any View] { - var result: [String: any View] = [:] - for (key, anyValue) in properties { - switch anyValue { - case let value as any View: - result[key.rawValue] = value - default: - break - } - } - return result - } - - /// Convert user provided properties (Any), to corresponding ComponentBinding - private var userBindings: [String: ComponentBinding] { - var result: [String: ComponentBinding] = [:] - - for (key, anyValue) in properties { - switch anyValue { - case let value as IntegerLiteralType: - result[key.rawValue] = ComponentBinding(.number(Double(value))) - case let bindingValue as Binding: - let propertyValueBinding = Binding { - PropertyValue.number(Double(bindingValue.wrappedValue)) - } set: { newValue in - if case .number(let value) = newValue { - bindingValue.wrappedValue = Int(value) - } - } - result[key.rawValue] = ComponentBinding(propertyValueBinding) - case let value as FloatLiteralType: - result[key.rawValue] = ComponentBinding(.number(value)) - case let bindingValue as Binding: - let propertyValueBinding = Binding { - PropertyValue.number(bindingValue.wrappedValue) - } set: { newValue in - if case .number(let value) = newValue { - bindingValue.wrappedValue = value - } - } - result[key.rawValue] = ComponentBinding(propertyValueBinding) - case let value as BooleanLiteralType: - result[key.rawValue] = ComponentBinding(.boolean(value)) - case let bindingValue as Binding: - let propertyValueBinding = Binding { - PropertyValue.boolean(bindingValue.wrappedValue) - } set: { newValue in - if case .boolean(let value) = newValue { - bindingValue.wrappedValue = value - } - } - result[key.rawValue] = ComponentBinding(propertyValueBinding) - case let value as StringLiteralType: - result[key.rawValue] = ComponentBinding(.text(value)) - case let bindingValue as Binding: - let propertyValueBinding = Binding { - PropertyValue.text(bindingValue.wrappedValue) - } set: { newValue in - if case .text(let value) = newValue { - bindingValue.wrappedValue = value - } - } - result[key.rawValue] = ComponentBinding(propertyValueBinding) - case let value as SwiftUI.Image: - result[key.rawValue] = ComponentBinding(.image(.inline(image: value))) - case let value as UIImage: - let image = SwiftUI.Image(uiImage: value) - result[key.rawValue] = ComponentBinding(.image(.inline(image: image))) - case is any View: - break - default: - logger.warning("Invalid value for property \"\(key)\". Property unused.") - break - } - } - - return result - } - public func component(_ componentName: ComponentName) -> JudoView { var result = self result.componentName = componentName diff --git a/Sources/JudoRenderer/Layers/ArtboardView.swift b/Sources/JudoRenderer/Layers/ArtboardView.swift index 386aa7e..7730f6b 100644 --- a/Sources/JudoRenderer/Layers/ArtboardView.swift +++ b/Sources/JudoRenderer/Layers/ArtboardView.swift @@ -21,26 +21,10 @@ import os.log struct ArtboardView: SwiftUI.View { var artboard: ArtboardNode - @StateObject private var componentState: ComponentState - - init(artboard: ArtboardNode, userBindings: [String: ComponentBinding], userViews: [String: any View]) { - self.artboard = artboard - self._componentState = StateObject( - wrappedValue: ComponentState( - bindings: userBindings, - views: userViews - ) - ) - } var body: some SwiftUI.View { - ForEach(orderedNodes, id: \.id) { node in + ForEach(artboard.children, id: \.id) { node in NodeView(node: node) } - .environmentObject(componentState) - } - - private var orderedNodes: [Node] { - artboard.children } } diff --git a/Sources/JudoRenderer/Layers/AsyncImageView.swift b/Sources/JudoRenderer/Layers/AsyncImageView.swift index 248a181..c0b1181 100644 --- a/Sources/JudoRenderer/Layers/AsyncImageView.swift +++ b/Sources/JudoRenderer/Layers/AsyncImageView.swift @@ -22,7 +22,7 @@ struct AsyncImageView: SwiftUI.View { @Environment(\.colorScheme) private var colorScheme @Environment(\.displayScale) private var displayScale @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings private var image: JudoDocument.AsyncImageLayer @@ -72,16 +72,20 @@ struct AsyncImageView: SwiftUI.View { } private var imageChildren: [Node] { - (image.children.first(where: { $0 is ContainerNode }) as! ContainerNode).children + imageContainers[0].children } private var placeholderChildren: [Node] { - (image.children.last(where: { $0 is ContainerNode }) as! ContainerNode).children + imageContainers[1].children + } + + private var imageContainers: [ContainerNode] { + image.children.compactMap({ $0 as? ContainerNode }) } private var resolvedURL: String { image.url.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Layers/ButtonView.swift b/Sources/JudoRenderer/Layers/ButtonView.swift index 58f5918..f487de2 100644 --- a/Sources/JudoRenderer/Layers/ButtonView.swift +++ b/Sources/JudoRenderer/Layers/ButtonView.swift @@ -58,14 +58,14 @@ private struct ButtonWithoutRole: SwiftUI.View { @Environment(\.actionHandlers) private var actionHandlers @Environment(\.presentationMode) private var presentationMode @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let actions: [Action] let content: Content var body: some SwiftUI.View { SwiftUI.Button { - Actions.perform(actions: actions, componentState: componentState, data: data, actionHandlers: actionHandlers) { + Actions.perform(actions: actions, componentBindings: componentBindings, data: data, actionHandlers: actionHandlers) { presentationMode.wrappedValue.dismiss() } openURL: { url in openURL(url) @@ -83,7 +83,7 @@ private struct ButtonWithRole: SwiftUI.View { @Environment(\.dismiss) private var dismiss // Only available in iOS 15+ @Environment(\.refresh) private var refresh // Only available in iOS 15+ @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let actions: [Action] let role: SwiftUI.ButtonRole? @@ -91,7 +91,7 @@ private struct ButtonWithRole: SwiftUI.View { var body: some SwiftUI.View { SwiftUI.Button(role: role) { - Actions.perform(actions: actions, componentState: componentState, data: data, actionHandlers: actionHandlers) { + Actions.perform(actions: actions, componentBindings: componentBindings, data: data, actionHandlers: actionHandlers) { dismiss() } openURL: { url in openURL(url) diff --git a/Sources/JudoRenderer/Layers/CollectionView.swift b/Sources/JudoRenderer/Layers/CollectionView.swift index c19ef42..a1c363f 100644 --- a/Sources/JudoRenderer/Layers/CollectionView.swift +++ b/Sources/JudoRenderer/Layers/CollectionView.swift @@ -18,7 +18,7 @@ import SwiftUI struct CollectionView: SwiftUI.View { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var collection: CollectionLayer var body: some SwiftUI.View { @@ -36,7 +36,7 @@ struct CollectionView: SwiftUI.View { private var items: [Any]? { collection.items( data: data, - propertyValues: componentState.propertyValues + propertyValues: componentBindings.propertyValues ) } } diff --git a/Sources/JudoRenderer/Layers/CombinedTextView.swift b/Sources/JudoRenderer/Layers/CombinedTextView.swift index ce7296f..75c0ac2 100644 --- a/Sources/JudoRenderer/Layers/CombinedTextView.swift +++ b/Sources/JudoRenderer/Layers/CombinedTextView.swift @@ -18,7 +18,7 @@ import SwiftUI struct CombinedTextView: SwiftUI.View { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.colorScheme) private var colorScheme @Environment(\.colorSchemeContrast) private var colorSchemeContrast @@ -34,7 +34,7 @@ struct CombinedTextView: SwiftUI.View { combinedLayers.reduce(Text("")) { result, value in let textLayer = value.0 let string = value.1 - return result + Text(string).modifiers(for: textLayer, documentNode: document, sizeCategory: sizeCategory, colorScheme: colorScheme, colorSchemeContrast: colorSchemeContrast, propertyValues: componentState.propertyValues, data: data) + return result + Text(string).modifiers(for: textLayer, documentNode: document, sizeCategory: sizeCategory, colorScheme: colorScheme, colorSchemeContrast: colorSchemeContrast, propertyValues: componentBindings.propertyValues, data: data) } } } @@ -49,7 +49,7 @@ struct CombinedTextView: SwiftUI.View { textLayers.map { textLayer in ( textLayer, - realizeText(textLayer.value, localized: true, strings: document.strings, propertyValues: componentState.propertyValues, data: data) + realizeText(textLayer.value, localized: true, strings: document.strings, propertyValues: componentBindings.propertyValues, data: data) ) } } diff --git a/Sources/JudoRenderer/Layers/ComponentInstanceView.swift b/Sources/JudoRenderer/Layers/ComponentInstanceView.swift index 61f6786..324e917 100644 --- a/Sources/JudoRenderer/Layers/ComponentInstanceView.swift +++ b/Sources/JudoRenderer/Layers/ComponentInstanceView.swift @@ -18,85 +18,32 @@ import SwiftUI /// A SwiftUI view for rendering a `ComponentInstance`. struct ComponentInstanceView: SwiftUI.View { - @Environment(\.document) private var document - @EnvironmentObject private var componentState: ComponentState - var componentInstance: ComponentInstanceLayer - + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data - @Environment(\.fetchedImage) private var fetchedImage - - init(componentInstance: ComponentInstanceLayer) { - self.componentInstance = componentInstance - } - - var body: some SwiftUI.View { - if let userView { - userView - } else { - ComponentBody(componentInstance: componentInstance) - } - } - - /// User view is provided in parameters and override component body - private var userView: AnyView? { - if let binding = componentInstance.value.binding, - case .property(let propertyName) = binding, - let userView = componentState.views[propertyName] - { - return AnyView(userView) - } - - return nil - } - -} - -private struct ComponentBody: View { @Environment(\.document) private var document - @EnvironmentObject private var componentState: ComponentState - var componentInstance: ComponentInstanceLayer - @Environment(\.data) private var data - @Environment(\.fetchedImage) private var fetchedImage - - init(componentInstance: ComponentInstanceLayer) { - self.componentInstance = componentInstance - } + var componentInstance: ComponentInstanceLayer - var body: some SwiftUI.View { - ForEach(orderedNodes, id: \.id) { - NodeView(node: $0) - } - .environmentObject( - ComponentState( - propertyValues: mainComponent?.properties.reduce(into: [:]) { partialResult, property in - partialResult[property.name] = property.value - } ?? [:], - overrides: componentInstance.overrides, - data: data, - fetchedImage: fetchedImage, - parentState: componentState + var body: some View { + /// Special handling for custom views since resolve only works for property values. + if case .property(let propertyName) = componentInstance.value.binding, + let view = componentBindings[propertyName]?.wrappedValue as? any View { + AnyView(view) + } else if let mainComponent { + MainComponentView( + mainComponent: mainComponent, + overrides: componentInstance.overrides ) - ) + } } private var mainComponent: MainComponentNode? { let mainComponentID = componentInstance.value.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - - return document.children.first { node in - switch node { - case let mainComponent as MainComponentNode: - return mainComponent.id == mainComponentID - default: - return false - } - } as? MainComponentNode - } - - private var orderedNodes: [Node] { - mainComponent?.children ?? [] + + let node = document.allNodes.first(where: { $0.id == mainComponentID }) + return node as? MainComponentNode } } diff --git a/Sources/JudoRenderer/Layers/ConditionalView.swift b/Sources/JudoRenderer/Layers/ConditionalView.swift index 475e98f..88c43e0 100644 --- a/Sources/JudoRenderer/Layers/ConditionalView.swift +++ b/Sources/JudoRenderer/Layers/ConditionalView.swift @@ -18,7 +18,7 @@ import SwiftUI struct ConditionalView: SwiftUI.View { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var conditional: ConditionalLayer var body: some SwiftUI.View { @@ -33,7 +33,7 @@ struct ConditionalView: SwiftUI.View { conditional.conditions.allSatisfy { condition in condition.isSatisfied( data: data, - propertyValues: componentState.propertyValues + propertyValues: componentBindings.propertyValues ) } } diff --git a/Sources/JudoRenderer/Layers/DataSourceView.swift b/Sources/JudoRenderer/Layers/DataSourceView.swift index 3fd07c6..1bf7bcb 100644 --- a/Sources/JudoRenderer/Layers/DataSourceView.swift +++ b/Sources/JudoRenderer/Layers/DataSourceView.swift @@ -19,7 +19,7 @@ import SwiftUI struct DataSourceView: SwiftUI.View { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @State private var fetchedData: Any? @State private var refreshTimer: Timer? @@ -72,7 +72,7 @@ struct DataSourceView: SwiftUI.View { } do { - let request = try dataSource.urlRequest(data: data, propertyValues: componentState.propertyValues) + let request = try dataSource.urlRequest(data: data, propertyValues: componentBindings.propertyValues) return URLSession.shared.dataPublisher(for: request) } catch { return Just(Result.failure(UnableToInterpolateDataSourceURLError())).eraseToAnyPublisher() diff --git a/Sources/JudoRenderer/Layers/ImageView.swift b/Sources/JudoRenderer/Layers/ImageView.swift index bf95063..5263460 100644 --- a/Sources/JudoRenderer/Layers/ImageView.swift +++ b/Sources/JudoRenderer/Layers/ImageView.swift @@ -22,7 +22,7 @@ struct ImageView: SwiftUI.View { @Environment(\.displayScale) private var displayScale @Environment(\.data) private var data @Environment(\.fetchedImage) private var fetchedImage - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings private var image: JudoDocument.ImageLayer @@ -33,7 +33,7 @@ struct ImageView: SwiftUI.View { var body: some SwiftUI.View { ImageReferenceView( imageReference: image.value.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data, fetchedImage: fetchedImage ), diff --git a/Sources/JudoRenderer/Layers/MainComponentView.swift b/Sources/JudoRenderer/Layers/MainComponentView.swift index bb94899..f567f89 100644 --- a/Sources/JudoRenderer/Layers/MainComponentView.swift +++ b/Sources/JudoRenderer/Layers/MainComponentView.swift @@ -21,32 +21,228 @@ import os.log /// A SwiftUI view for rendering a `MainComponentNode`. struct MainComponentView: SwiftUI.View { - var component: MainComponentNode - @StateObject private var componentState: ComponentState - - /// Initialize with MainComponentNode and user provided bindings (parameters) - init(component: MainComponentNode, userBindings: [String: ComponentBinding], userViews: [String: any View]) { - self.component = component - self._componentState = StateObject( - wrappedValue: ComponentState( - bindings: component.properties - .reduce(into: [:]) { partialResult, property in - partialResult[property.name] = ComponentBinding(property.value) - } - .merging(userBindings, uniquingKeysWith: { (_, new) in new }), - views: userViews - ) - ) + @Environment(\.data) private var data + @Environment(\.document) private var document + @Environment(\.fetchedImage) private var fetchedImage + @Environment(\.componentBindings) private var componentBindings + + @State private var componentState: [String: Any] = [:] + + var mainComponent: MainComponentNode + var overrides: Overrides = [:] + var userProperties: [String: Any] = [:] + + init(mainComponent: MainComponentNode, userProperties: [String : Any]) { + self.mainComponent = mainComponent + self.userProperties = userProperties } - + + init(mainComponent: MainComponentNode, overrides: Overrides) { + self.mainComponent = mainComponent + self.overrides = overrides + } + var body: some SwiftUI.View { - ForEach(orderedNodes, id: \.id) { node in - NodeView(node: node) + ForEach(mainComponent.children, id: \.id) { + NodeView(node: $0) } - .environmentObject(componentState) + .environment(\.componentBindings, resolvedComponentBindings) } - - private var orderedNodes: [Node] { - component.children + + private var resolvedComponentBindings: ComponentBindings { + mainComponent.properties.reduce(into: [:]) { partialResult, property in + partialResult[property.name] = Binding( + get: { + /// 1. Check if `componentState` has a value. This means a control (e.g. TextField, Toggle or Slider) has updated the value from it's default value. This takes precedent. + if let value = componentState[property.name] { + return value + } + + /// 2. Check if the user has provided a value or binding to a value when initiating the JudoView. + switch (property.value, userProperties[property.name]) { + case (.number, let value as IntegerLiteralType): + return Double(value) + case (.number, let binding as Binding): + return Double(binding.wrappedValue) + case (.number, let value as FloatLiteralType): + return Double(value) + case (.number, let binding as Binding): + return binding.wrappedValue + case (.boolean, let value as BooleanLiteralType): + return Bool(value) + case (.boolean, let binding as Binding): + return binding.wrappedValue + case (.text, let value as StringLiteralType): + return String(value) + case (.text, let binding as Binding): + return binding.wrappedValue + case (.image, let value as SwiftUI.Image): + return ImageReference.inline(image: value) + case (.image, let value as UIImage): + let image = SwiftUI.Image(uiImage: value) + return ImageReference.inline(image: image) + case (.component, let value as any View): + return value + default: + break + } + + /// 3. Check if the default value has been overridden + switch (property.value, overrides[property.name]) { + case (.text(let defaultValue), .text(let value)): + let resolvedValue = value.resolve( + propertyValues: componentBindings.propertyValues, + data: data, + fetchedImage: fetchedImage + ) + + return resolvedValue ?? defaultValue + case (.number(let defaultValue), .number(let value)): + let resolvedValue = value.resolve( + propertyValues: componentBindings.propertyValues, + data: data, + fetchedImage: fetchedImage + ) + + return resolvedValue ?? defaultValue + case (.boolean(let defaultValue), .boolean(let value)): + let resolvedValue = value.resolve( + propertyValues: componentBindings.propertyValues, + data: data, + fetchedImage: fetchedImage + ) + + return resolvedValue ?? defaultValue + case (.image(let defaultValue), .image(let value)): + let resolvedValue = value.resolve( + propertyValues: componentBindings.propertyValues, + data: data, + fetchedImage: fetchedImage + ) + + return resolvedValue ?? defaultValue + case (.component(let defaultValue), .component(let value)): + /// Special handling for custom views since resolve only works for property values. + if case .property(let propertyName) = value.binding, + let view = componentBindings[propertyName]?.wrappedValue as? any View { + return view + } + + let resolvedValue = value.resolve( + propertyValues: componentBindings.propertyValues, + data: data, + fetchedImage: fetchedImage + ) + + return resolvedValue ?? defaultValue + default: + break + } + + /// 4. Return the default value defined on the main component itself. + switch property.value { + case .text(let defaultValue): + return defaultValue + case .number(let defaultValue): + return defaultValue + case .boolean(let defaultValue): + return defaultValue + case .image(let defaultValue): + return defaultValue + case .component(let defaultValue): + return defaultValue + case .video(let defaultValue): + return defaultValue + case .computed(let defaultValue): + switch defaultValue { + case .text(let expression): + return expression + case .number(let expression): + return expression + case .boolean(let expression): + return expression + } + } + }, + set: { newValue in + /// 1. Check if the user has supplied a binding + switch (property.value, userProperties[property.name]) { + case (.text, let binding as Binding): + guard let value = newValue as? String else { + assertionFailure("Wrong value type—expected text") + break + } + + binding.wrappedValue = value + return + case (.number, let binding as Binding): + guard let value = newValue as? Double else { + assertionFailure("Wrong value type—expected number") + break + } + + binding.wrappedValue = Int(value) + return + case (.number, let binding as Binding): + guard let value = newValue as? Double else { + assertionFailure("Wrong value type—expected number") + break + } + + binding.wrappedValue = value + return + case (.boolean, let binding as Binding): + guard let value = newValue as? Bool else { + assertionFailure("Wrong value type—expected boolean") + break + } + + binding.wrappedValue = value + return + default: + break + } + + /// 2. Check if the property has been overridden with a binding + switch (property.value, overrides[property.name]) { + case (.text, .text(let variable)): + if case .property(let propertyName) = variable.binding, let binding = componentBindings[propertyName] { + guard let value = newValue as? String else { + assertionFailure("Wrong value type—expected text") + break + } + + binding.wrappedValue = value + return + } + case (.number, .number(let variable)): + if case .property(let propertyName) = variable.binding, let binding = componentBindings[propertyName] { + guard let value = newValue as? Double else { + assertionFailure("Wrong value type—expected number") + break + } + + binding.wrappedValue = value + return + } + case (.boolean, .boolean(let variable)): + if case .property(let propertyName) = variable.binding, let binding = componentBindings[propertyName] { + guard let value = newValue as? Bool else { + assertionFailure("Wrong value type—expected boolean") + break + } + + binding.wrappedValue = value + return + } + default: + break + } + + // 3. Update component state + componentState[property.name] = newValue + } + ) + } } } diff --git a/Sources/JudoRenderer/Layers/NavigationLinkView.swift b/Sources/JudoRenderer/Layers/NavigationLinkView.swift index ba6a27e..52ea6ec 100644 --- a/Sources/JudoRenderer/Layers/NavigationLinkView.swift +++ b/Sources/JudoRenderer/Layers/NavigationLinkView.swift @@ -19,11 +19,10 @@ import SwiftUI struct NavigationLinkView: SwiftUI.View { @Environment(\.assetManager) private var assetManager @Environment(\.actionHandlers) private var actionHandlers + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data @Environment(\.document) private var document @Environment(\.fetchedImage) private var fetchedImage - - @EnvironmentObject private var componentState: ComponentState var navigationLink: JudoDocument.NavigationLinkLayer var body: some SwiftUI.View { @@ -33,10 +32,10 @@ struct NavigationLinkView: SwiftUI.View { } .environment(\.assetManager, assetManager) .environment(\.actionHandlers, actionHandlers) + .environment(\.componentBindings, componentBindings) .environment(\.data, data) .environment(\.document, document) .environment(\.fetchedImage, fetchedImage) - .environmentObject(componentState) } label: { ForEach(labelChildren, id: \.id) { NodeView(node: $0) @@ -45,10 +44,14 @@ struct NavigationLinkView: SwiftUI.View { } private var labelChildren: [Node] { - (navigationLink.children.first(where: { $0 is ContainerNode }) as! ContainerNode).children + navigationLinkContainers[0].children } private var destinationChildren: [Node] { - (navigationLink.children.last(where: { $0 is ContainerNode }) as! ContainerNode).children + navigationLinkContainers[1].children + } + + private var navigationLinkContainers: [ContainerNode] { + navigationLink.children.compactMap({ $0 as? ContainerNode }) } } diff --git a/Sources/JudoRenderer/Layers/PickerView.swift b/Sources/JudoRenderer/Layers/PickerView.swift index 93b8a8b..70ceeaf 100644 --- a/Sources/JudoRenderer/Layers/PickerView.swift +++ b/Sources/JudoRenderer/Layers/PickerView.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct PickerView: SwiftUI.View { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data private var isTextBinding: Bool @@ -55,7 +55,7 @@ struct PickerView: SwiftUI.View { @ViewBuilder private func row(for option: PickerOption) -> some View { RealizeText(option.title ?? "") { title in - if let imageReference = option.icon?.forceResolve(propertyValues: componentState.propertyValues, data: data) { + if let imageReference = option.icon?.forceResolve(propertyValues: componentBindings.propertyValues, data: data) { if title.isEmpty { ImageReferenceView( imageReference: imageReference, @@ -83,21 +83,21 @@ struct PickerView: SwiftUI.View { private var stringSelectionBinding: Binding { Binding { - picker.textSelection?.forceResolve(propertyValues: componentState.propertyValues, data: data) + picker.textSelection?.forceResolve(propertyValues: componentBindings.propertyValues, data: data) } set: { newValue in guard let newValue else { return } if case let .property(name) = picker.textSelection?.binding { - componentState.bindings[name]?.value = .text(newValue) + componentBindings[name]?.wrappedValue = newValue } } } private func textTag(_ option: PickerOption) -> String? { if let textValue = option.textValue { - return textValue.forceResolve(propertyValues: componentState.propertyValues, data: data) + return textValue.forceResolve(propertyValues: componentBindings.propertyValues, data: data) } return nil @@ -105,21 +105,21 @@ struct PickerView: SwiftUI.View { private var numberSelectionBinding: Binding { Binding { - picker.numberSelection?.forceResolve(propertyValues: componentState.propertyValues, data: data) + picker.numberSelection?.forceResolve(propertyValues: componentBindings.propertyValues, data: data) } set: { newValue in guard let newValue else { return } if case let .property(name) = picker.numberSelection?.binding { - componentState.bindings[name]?.value = .number(newValue) + componentBindings[name]?.wrappedValue = newValue } } } private func numberTag(_ option: PickerOption) -> Double? { if let numberValue = option.numberValue { - return numberValue.forceResolve(propertyValues: componentState.propertyValues, data: data) + return numberValue.forceResolve(propertyValues: componentBindings.propertyValues, data: data) } return nil diff --git a/Sources/JudoRenderer/Layers/RoundedRectangleView.swift b/Sources/JudoRenderer/Layers/RoundedRectangleView.swift index caca15f..f6eef89 100644 --- a/Sources/JudoRenderer/Layers/RoundedRectangleView.swift +++ b/Sources/JudoRenderer/Layers/RoundedRectangleView.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct RoundedRectangleView: SwiftUI.View { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var roundedRectangle: JudoDocument.RoundedRectangleLayer @@ -29,7 +29,7 @@ struct RoundedRectangleView: SwiftUI.View { private var cornerRadius: Double { roundedRectangle.cornerRadius.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Layers/SecureFieldView.swift b/Sources/JudoRenderer/Layers/SecureFieldView.swift index 41b55e6..5e2bdb3 100644 --- a/Sources/JudoRenderer/Layers/SecureFieldView.swift +++ b/Sources/JudoRenderer/Layers/SecureFieldView.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct SecureFieldView: SwiftUI.View { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var secureField: JudoDocument.SecureFieldLayer @@ -34,7 +34,7 @@ struct SecureFieldView: SwiftUI.View { value } set: { newValue in if case .property(let name) = secureField.text.binding { - componentState.bindings[name]?.value = .text(newValue) + componentBindings[name]?.wrappedValue = newValue } } } diff --git a/Sources/JudoRenderer/Layers/SliderView.swift b/Sources/JudoRenderer/Layers/SliderView.swift index 44d0633..a568fa1 100644 --- a/Sources/JudoRenderer/Layers/SliderView.swift +++ b/Sources/JudoRenderer/Layers/SliderView.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct SliderView: SwiftUI.View { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var slider: JudoDocument.SliderLayer @@ -39,7 +39,7 @@ struct SliderView: SwiftUI.View { @ViewBuilder private var sliderView: some View { RealizeText(slider.label) { label in - switch (range, slider.step?.forceResolve(propertyValues: componentState.propertyValues, data: data)) { + switch (range, slider.step?.forceResolve(propertyValues: componentBindings.propertyValues, data: data)) { case (.some(let range), .some(let step)): SwiftUI.Slider(value: valueBinding, in: range, step: step) { SwiftUI.Text(label) @@ -62,7 +62,7 @@ struct SliderView: SwiftUI.View { RealizeText(slider.label) { label in RealizeText(slider.minLabel ?? "") { minLabel in RealizeText(slider.maxLabel ?? "") { maxLabel in - switch (range, slider.step?.forceResolve(propertyValues: componentState.propertyValues, data: data)) { + switch (range, slider.step?.forceResolve(propertyValues: componentBindings.propertyValues, data: data)) { case (.some(let range), .some(let step)): SwiftUI.Slider(value: valueBinding, in: range, step: step) { SwiftUI.Text(label) @@ -95,14 +95,14 @@ struct SliderView: SwiftUI.View { private var valueBinding: Binding { Binding { - slider.value.forceResolve(propertyValues: componentState.propertyValues, data: data) + slider.value.forceResolve(propertyValues: componentBindings.propertyValues, data: data) } set: { newValue in if case .property(let name) = slider.value.binding { - switch componentState.bindings[name]?.value { - case .number: - componentState.bindings[name]?.value = .number(newValue) - case .text: - componentState.bindings[name]?.value = .text(newValue.description) + switch componentBindings[name]?.wrappedValue { + case is Double: + componentBindings[name]?.wrappedValue = newValue + case is String: + componentBindings[name]?.wrappedValue = newValue.description default: break } @@ -111,7 +111,7 @@ struct SliderView: SwiftUI.View { } private var range: ClosedRange? { - guard let minValue = slider.minValue?.forceResolve(propertyValues: componentState.propertyValues, data: data), let maxValue = slider.maxValue?.forceResolve(propertyValues: componentState.propertyValues, data: data) else { return nil } + guard let minValue = slider.minValue?.forceResolve(propertyValues: componentBindings.propertyValues, data: data), let maxValue = slider.maxValue?.forceResolve(propertyValues: componentBindings.propertyValues, data: data) else { return nil } if minValue < maxValue { return minValue...maxValue } else { diff --git a/Sources/JudoRenderer/Layers/SpacerView.swift b/Sources/JudoRenderer/Layers/SpacerView.swift index 20f5cc6..893ce92 100644 --- a/Sources/JudoRenderer/Layers/SpacerView.swift +++ b/Sources/JudoRenderer/Layers/SpacerView.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct SpacerView: SwiftUI.View { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var spacer: JudoDocument.SpacerLayer @@ -30,7 +30,7 @@ struct SpacerView: SwiftUI.View { private var minLength: Double? { spacer.minLength?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Layers/StepperView.swift b/Sources/JudoRenderer/Layers/StepperView.swift index 0d7334c..6267ad0 100644 --- a/Sources/JudoRenderer/Layers/StepperView.swift +++ b/Sources/JudoRenderer/Layers/StepperView.swift @@ -17,14 +17,14 @@ import JudoDocument import SwiftUI struct StepperView: SwiftUI.View { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var stepper: JudoDocument.StepperLayer var body: some SwiftUI.View { RealizeText(stepper.label) { label in - switch (range, stepper.step?.forceResolve(propertyValues: componentState.propertyValues, data: data)) { + switch (range, stepper.step?.forceResolve(propertyValues: componentBindings.propertyValues, data: data)) { case (.some(let range), .some(let step)): SwiftUI.Stepper(label, value: valueBinding, in: range, step: step) case (.some(let range), .none): @@ -40,18 +40,18 @@ struct StepperView: SwiftUI.View { private var valueBinding: Binding { Binding { stepper.value.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } set: { newValue in if case .property(let name) = stepper.value.binding { - componentState.bindings[name]?.value = .number(newValue) + componentBindings[name]?.wrappedValue = newValue } } } private var range: ClosedRange? { - guard let minValue = stepper.minValue?.forceResolve(propertyValues: componentState.propertyValues, data: data), let maxValue = stepper.maxValue?.forceResolve(propertyValues: componentState.propertyValues, data: data) else { return nil } + guard let minValue = stepper.minValue?.forceResolve(propertyValues: componentBindings.propertyValues, data: data), let maxValue = stepper.maxValue?.forceResolve(propertyValues: componentBindings.propertyValues, data: data) else { return nil } if minValue < maxValue { return minValue...maxValue } else { diff --git a/Sources/JudoRenderer/Layers/TextFieldView.swift b/Sources/JudoRenderer/Layers/TextFieldView.swift index 33b7497..2960729 100644 --- a/Sources/JudoRenderer/Layers/TextFieldView.swift +++ b/Sources/JudoRenderer/Layers/TextFieldView.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct TextFieldView: SwiftUI.View { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var textField: JudoDocument.TextFieldLayer @@ -30,8 +30,8 @@ struct TextFieldView: SwiftUI.View { RealizeText(textField.text, localized: false) { text in if case .property(let name) = textField.text.binding { - switch componentState.bindings[name]?.value { - case .number: + switch componentBindings[name]?.wrappedValue { + case is Double: SwiftUI.TextField(title, value: numberValueBinding(text), formatter: NumberFormatter.allowsFloatsNumberFormatter) default: textField(title: title, text: text) @@ -56,9 +56,9 @@ struct TextFieldView: SwiftUI.View { value } set: { newValue in if case .property(let name) = textField.text.binding { - switch componentState.bindings[name]?.value { - case .text: - componentState.bindings[name]?.value = .text(newValue) + switch componentBindings[name]?.wrappedValue { + case is String: + componentBindings[name]?.wrappedValue = newValue default: break } @@ -71,10 +71,10 @@ struct TextFieldView: SwiftUI.View { Double(value) } set: { newValue in if case .property(let name) = textField.text.binding { - switch componentState.bindings[name]?.value { - case .number: + switch componentBindings[name]?.wrappedValue { + case is Double: if let newValue { - componentState.bindings[name]?.value = .number(newValue) + componentBindings[name]?.wrappedValue = newValue } default: break diff --git a/Sources/JudoRenderer/Layers/ToggleView.swift b/Sources/JudoRenderer/Layers/ToggleView.swift index ad795f6..925a417 100644 --- a/Sources/JudoRenderer/Layers/ToggleView.swift +++ b/Sources/JudoRenderer/Layers/ToggleView.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct ToggleView: SwiftUI.View { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var toggle: JudoDocument.ToggleLayer @@ -31,12 +31,12 @@ struct ToggleView: SwiftUI.View { private var isOnBinding: Binding { Binding { toggle.isOn.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } set: { newValue in if case .property(let name) = toggle.isOn.binding { - componentState.bindings[name]?.value = .boolean(newValue) + componentBindings[name]?.wrappedValue = newValue } } diff --git a/Sources/JudoRenderer/Layers/VideoPlayerView.swift b/Sources/JudoRenderer/Layers/VideoPlayerView.swift index 58607ad..4419fca 100644 --- a/Sources/JudoRenderer/Layers/VideoPlayerView.swift +++ b/Sources/JudoRenderer/Layers/VideoPlayerView.swift @@ -19,7 +19,7 @@ import SwiftUI struct VideoPlayerView: SwiftUI.View { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var videoPlayer: JudoDocument.VideoPlayerLayer @@ -33,7 +33,7 @@ struct VideoPlayerView: SwiftUI.View { private var video: Video { videoPlayer.video.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/AccessibilitySortPriorityViewModifier.swift b/Sources/JudoRenderer/Modifiers/AccessibilitySortPriorityViewModifier.swift index de77191..a8ae6ef 100644 --- a/Sources/JudoRenderer/Modifiers/AccessibilitySortPriorityViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/AccessibilitySortPriorityViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct AccessibilitySortPriorityViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: AccessibilitySortPriorityModifier @@ -29,7 +29,7 @@ struct AccessibilitySortPriorityViewModifier: SwiftUI.ViewModifier { private var value: Double { modifier.sortPriority.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/AspectRatioViewModifier.swift b/Sources/JudoRenderer/Modifiers/AspectRatioViewModifier.swift index 770ee76..a2c2eaf 100644 --- a/Sources/JudoRenderer/Modifiers/AspectRatioViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/AspectRatioViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct AspectRatioViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: AspectRatioModifier @@ -32,7 +32,7 @@ struct AspectRatioViewModifier: SwiftUI.ViewModifier { private var ratio: Double? { modifier.ratio?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/AutoCorrectionDisabledViewModifier.swift b/Sources/JudoRenderer/Modifiers/AutoCorrectionDisabledViewModifier.swift index 3fad90b..22d04e9 100644 --- a/Sources/JudoRenderer/Modifiers/AutoCorrectionDisabledViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/AutoCorrectionDisabledViewModifier.swift @@ -18,7 +18,7 @@ import SwiftUI struct AutocorrectionDisabledViewModifier: SwiftUI.ViewModifier { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var modifier: JudoDocument.AutocorrectionDisabledModifier @@ -26,7 +26,7 @@ struct AutocorrectionDisabledViewModifier: SwiftUI.ViewModifier { content .autocorrectionDisabled( modifier.isDisabled.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) ) diff --git a/Sources/JudoRenderer/Modifiers/BlurViewModifier.swift b/Sources/JudoRenderer/Modifiers/BlurViewModifier.swift index 6df5baf..335eb90 100644 --- a/Sources/JudoRenderer/Modifiers/BlurViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/BlurViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct BlurViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: BlurModifier @@ -29,14 +29,14 @@ struct BlurViewModifier: SwiftUI.ViewModifier { private var isOpaque: Bool { modifier.isOpaque.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } private var radius: CGFloat { modifier.radius.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/BorderViewModifier.swift b/Sources/JudoRenderer/Modifiers/BorderViewModifier.swift index 0c913c7..5599eae 100644 --- a/Sources/JudoRenderer/Modifiers/BorderViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/BorderViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct BorderViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: BorderModifier @@ -30,7 +30,7 @@ struct BorderViewModifier: SwiftUI.ViewModifier { private var width: Double { modifier.width.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/ClipShapeViewModifier.swift b/Sources/JudoRenderer/Modifiers/ClipShapeViewModifier.swift index 2eaba97..db67967 100644 --- a/Sources/JudoRenderer/Modifiers/ClipShapeViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/ClipShapeViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct ClipShapeViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: ClipShapeModifier @@ -67,13 +67,13 @@ struct ClipShapeViewModifier: SwiftUI.ViewModifier { } private var fillStyle: SwiftUI.FillStyle { - let eoFill = modifier.isEvenOddRule.forceResolve(propertyValues: componentState.propertyValues, data: data) - let antialiased = modifier.isAntialiased.forceResolve(propertyValues: componentState.propertyValues, data: data) + let eoFill = modifier.isEvenOddRule.forceResolve(propertyValues: componentBindings.propertyValues, data: data) + let antialiased = modifier.isAntialiased.forceResolve(propertyValues: componentBindings.propertyValues, data: data) return FillStyle(eoFill: eoFill, antialiased: antialiased) } private func cornerRadius(_ cornerRadius: Variable) -> Double { - cornerRadius.forceResolve(propertyValues: componentState.propertyValues, data: data) + cornerRadius.forceResolve(propertyValues: componentBindings.propertyValues, data: data) } } diff --git a/Sources/JudoRenderer/Modifiers/ClippedViewModifier.swift b/Sources/JudoRenderer/Modifiers/ClippedViewModifier.swift index d03ae26..54cf4eb 100644 --- a/Sources/JudoRenderer/Modifiers/ClippedViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/ClippedViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct ClippedViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: ClippedModifier @@ -29,7 +29,7 @@ struct ClippedViewModifier: SwiftUI.ViewModifier { private var isAntialiased: Bool { modifier.isAntialiased.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/ContainerRelativeFrameViewModifier.swift b/Sources/JudoRenderer/Modifiers/ContainerRelativeFrameViewModifier.swift index b639439..4ec7a36 100644 --- a/Sources/JudoRenderer/Modifiers/ContainerRelativeFrameViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/ContainerRelativeFrameViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct ContainerRelativeFrameViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: ContainerRelativeFrameModifier @@ -51,21 +51,21 @@ struct ContainerRelativeFrameViewModifier: SwiftUI.ViewModifier { private var count: Int { Int(modifier.count.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data )) } private var span: Int { Int(modifier.span.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data )) } private var spacing: Double { modifier.spacing.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/FixedSizeViewModifier.swift b/Sources/JudoRenderer/Modifiers/FixedSizeViewModifier.swift index a5db0de..ae33ad5 100644 --- a/Sources/JudoRenderer/Modifiers/FixedSizeViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/FixedSizeViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct FixedSizeViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: FixedSizeModifier @@ -29,14 +29,14 @@ struct FixedSizeViewModifier: SwiftUI.ViewModifier { private var horizontal: Bool { modifier.horizontal.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } private var vertical: Bool { modifier.vertical.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/FontViewModifier.swift b/Sources/JudoRenderer/Modifiers/FontViewModifier.swift index 0458713..1746b1b 100644 --- a/Sources/JudoRenderer/Modifiers/FontViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/FontViewModifier.swift @@ -19,7 +19,7 @@ import SwiftUI struct FontViewModifier: SwiftUI.ViewModifier { @Environment(\.document) private var document - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data @Environment(\.sizeCategory) private var sizeCategory @@ -33,7 +33,7 @@ struct FontViewModifier: SwiftUI.ViewModifier { modifier.font, documentNode: document, sizeCategory: sizeCategory, - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) ) diff --git a/Sources/JudoRenderer/Modifiers/FrameViewModifier.swift b/Sources/JudoRenderer/Modifiers/FrameViewModifier.swift index 0a61d1f..6b27b96 100644 --- a/Sources/JudoRenderer/Modifiers/FrameViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/FrameViewModifier.swift @@ -18,7 +18,7 @@ import SwiftUI struct FrameViewModifier: SwiftUI.ViewModifier { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var modifier: JudoDocument.FrameModifier @@ -41,7 +41,7 @@ struct FrameViewModifier: SwiftUI.ViewModifier { private var width: CGFloat? { let resolvedValue = modifier.width?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -50,7 +50,7 @@ struct FrameViewModifier: SwiftUI.ViewModifier { private var maxWidth: CGFloat? { let resolvedValue = modifier.maxWidth?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -59,7 +59,7 @@ struct FrameViewModifier: SwiftUI.ViewModifier { private var minWidth: CGFloat? { let resolvedValue = modifier.minWidth?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -68,7 +68,7 @@ struct FrameViewModifier: SwiftUI.ViewModifier { private var height: CGFloat? { let resolvedValue = modifier.height?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -77,7 +77,7 @@ struct FrameViewModifier: SwiftUI.ViewModifier { private var maxHeight: CGFloat? { let resolvedValue = modifier.maxHeight?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -86,7 +86,7 @@ struct FrameViewModifier: SwiftUI.ViewModifier { private var minHeight: CGFloat? { let resolvedValue = modifier.minHeight?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) diff --git a/Sources/JudoRenderer/Modifiers/FullScreenCoverViewModifier.swift b/Sources/JudoRenderer/Modifiers/FullScreenCoverViewModifier.swift index 54353a7..2855109 100644 --- a/Sources/JudoRenderer/Modifiers/FullScreenCoverViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/FullScreenCoverViewModifier.swift @@ -38,14 +38,14 @@ private struct FullScreenCover_iOS15_Modifier: ViewModifier { @Environment(\.dismiss) private var dismiss // Only available in iOS 15+ @Environment(\.refresh) private var refresh // Only available in iOS 15+ @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var modifier: FullScreenCoverModifier func body(content: Content) -> some View { content .fullScreenCover(isPresented: isPresented, onDismiss: { - Actions.perform(actions: modifier.onDismissActions, componentState: componentState, data: data, actionHandlers: actionHandlers) { + Actions.perform(actions: modifier.onDismissActions, componentBindings: componentBindings, data: data, actionHandlers: actionHandlers) { dismiss() } openURL: { url in openURL(url) @@ -75,12 +75,12 @@ private struct FullScreenCover_iOS15_Modifier: ViewModifier { private var isPresented: Binding { Binding { modifier.isPresented.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } set: { newValue in if case .property(let name) = modifier.isPresented.binding { - componentState.bindings[name]?.value = .boolean(newValue) + componentBindings[name]?.wrappedValue = newValue } } } @@ -90,7 +90,7 @@ private struct FullScreenCover_Legacy_Modifier: SwiftUI.ViewModifier { @Environment(\.openURL) private var openURL @Environment(\.actionHandlers) private var actionHandlers @Environment(\.presentationMode) private var presentationMode - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: FullScreenCoverModifier @@ -98,7 +98,7 @@ private struct FullScreenCover_Legacy_Modifier: SwiftUI.ViewModifier { func body(content: Content) -> some View { content .fullScreenCover(isPresented: isPresented, onDismiss: { - Actions.perform(actions: modifier.onDismissActions, componentState: componentState, data: data, actionHandlers: actionHandlers) { + Actions.perform(actions: modifier.onDismissActions, componentBindings: componentBindings, data: data, actionHandlers: actionHandlers) { presentationMode.wrappedValue.dismiss() } openURL: { url in openURL(url) @@ -124,12 +124,12 @@ private struct FullScreenCover_Legacy_Modifier: SwiftUI.ViewModifier { private var isPresented: Binding { Binding { modifier.isPresented.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } set: { newValue in if case .property(let name) = modifier.isPresented.binding { - componentState.bindings[name]?.value = .boolean(newValue) + componentBindings[name]?.wrappedValue = newValue } } } diff --git a/Sources/JudoRenderer/Modifiers/InteractiveDismissDisabledViewModifier.swift b/Sources/JudoRenderer/Modifiers/InteractiveDismissDisabledViewModifier.swift index 2130acd..c25a97d 100644 --- a/Sources/JudoRenderer/Modifiers/InteractiveDismissDisabledViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/InteractiveDismissDisabledViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct InteractiveDismissDisabledViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: InteractiveDismissDisabledModifier @@ -29,7 +29,7 @@ struct InteractiveDismissDisabledViewModifier: SwiftUI.ViewModifier { private var isDisabled: Bool { modifier.isDisabled.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/KerningViewModifier.swift b/Sources/JudoRenderer/Modifiers/KerningViewModifier.swift index afb4e52..3a8ea1c 100644 --- a/Sources/JudoRenderer/Modifiers/KerningViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/KerningViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct KerningViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: JudoDocument.KerningModifier @@ -29,7 +29,7 @@ struct KerningViewModifier: SwiftUI.ViewModifier { private var kerningValue: CGFloat { modifier.kerning.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/LayoutPriorityViewModifier.swift b/Sources/JudoRenderer/Modifiers/LayoutPriorityViewModifier.swift index 61bb977..197cdca 100644 --- a/Sources/JudoRenderer/Modifiers/LayoutPriorityViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/LayoutPriorityViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct LayoutPriorityViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: LayoutPriorityModifier @@ -29,7 +29,7 @@ struct LayoutPriorityViewModifier: SwiftUI.ViewModifier { private var priority: Double { modifier.priority.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/LineLimitViewModifier.swift b/Sources/JudoRenderer/Modifiers/LineLimitViewModifier.swift index 0321e73..0f02b86 100644 --- a/Sources/JudoRenderer/Modifiers/LineLimitViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/LineLimitViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct LineLimitViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: LineLimitModifier @@ -57,7 +57,7 @@ struct LineLimitViewModifier: SwiftUI.ViewModifier { private var minValue: Int? { let resolvedValue = modifier.min?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -66,7 +66,7 @@ struct LineLimitViewModifier: SwiftUI.ViewModifier { private var maxValue: Int? { let resolvedValue = modifier.max?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) diff --git a/Sources/JudoRenderer/Modifiers/MinimumScaleFactorViewModifier.swift b/Sources/JudoRenderer/Modifiers/MinimumScaleFactorViewModifier.swift index a4b5dbe..4e8f5b2 100644 --- a/Sources/JudoRenderer/Modifiers/MinimumScaleFactorViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/MinimumScaleFactorViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct MinimumScaleFactorViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: MinimumScaleFactorModifier @@ -29,7 +29,7 @@ struct MinimumScaleFactorViewModifier: SwiftUI.ViewModifier { private var scaleFactor: Double { let scaleFactor = modifier.scaleFactor.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) diff --git a/Sources/JudoRenderer/Modifiers/OffsetViewModifier.swift b/Sources/JudoRenderer/Modifiers/OffsetViewModifier.swift index 6c608e0..27444a4 100644 --- a/Sources/JudoRenderer/Modifiers/OffsetViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/OffsetViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct OffsetViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: OffsetModifier @@ -30,11 +30,11 @@ struct OffsetViewModifier: SwiftUI.ViewModifier { private var size: CGSize { CGSize( width: modifier.width.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ), height: modifier.height.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) ) diff --git a/Sources/JudoRenderer/Modifiers/OnAppearViewModifier.swift b/Sources/JudoRenderer/Modifiers/OnAppearViewModifier.swift index 01424a3..58d9abb 100644 --- a/Sources/JudoRenderer/Modifiers/OnAppearViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/OnAppearViewModifier.swift @@ -37,7 +37,7 @@ private struct OnAppear_iOS15_Modifier: SwiftUI.ViewModifier { @Environment(\.dismiss) private var dismiss // Only available in iOS 15+ @Environment(\.refresh) private var refresh // Only available in iOS 15+ @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let actions: [Action] @@ -46,7 +46,7 @@ private struct OnAppear_iOS15_Modifier: SwiftUI.ViewModifier { .onAppear { Actions.perform( actions: actions, - componentState: componentState, + componentBindings: componentBindings, data: data, actionHandlers: actionHandlers ) { @@ -67,7 +67,7 @@ private struct OnAppear_Legacy_Modifier: SwiftUI.ViewModifier { @Environment(\.actionHandlers) private var actionHandlers @Environment(\.presentationMode) private var presentationMode @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let actions: [Action] @@ -76,7 +76,7 @@ private struct OnAppear_Legacy_Modifier: SwiftUI.ViewModifier { .onAppear { Actions.perform( actions: actions, - componentState: componentState, + componentBindings: componentBindings, data: data, actionHandlers: actionHandlers ) { diff --git a/Sources/JudoRenderer/Modifiers/OnDisappearViewModifier.swift b/Sources/JudoRenderer/Modifiers/OnDisappearViewModifier.swift index 9d19e44..d307b1d 100644 --- a/Sources/JudoRenderer/Modifiers/OnDisappearViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/OnDisappearViewModifier.swift @@ -37,7 +37,7 @@ private struct OnDisappear_iOS15_Modifier: SwiftUI.ViewModifier { @Environment(\.dismiss) private var dismiss // Only available in iOS 15+ @Environment(\.refresh) private var refresh // Only available in iOS 15+ @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let actions: [Action] @@ -46,7 +46,7 @@ private struct OnDisappear_iOS15_Modifier: SwiftUI.ViewModifier { .onDisappear { Actions.perform( actions: actions, - componentState: componentState, + componentBindings: componentBindings, data: data, actionHandlers: actionHandlers ) { @@ -67,7 +67,7 @@ private struct OnDisappear_Legacy_Modifier: SwiftUI.ViewModifier { @Environment(\.actionHandlers) private var actionHandlers @Environment(\.presentationMode) private var presentationMode @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let actions: [Action] @@ -76,7 +76,7 @@ private struct OnDisappear_Legacy_Modifier: SwiftUI.ViewModifier { .onDisappear { Actions.perform( actions: actions, - componentState: componentState, + componentBindings: componentBindings, data: data, actionHandlers: actionHandlers ) { diff --git a/Sources/JudoRenderer/Modifiers/OnTapGestureViewModifier.swift b/Sources/JudoRenderer/Modifiers/OnTapGestureViewModifier.swift index a5714bb..e5b5d70 100644 --- a/Sources/JudoRenderer/Modifiers/OnTapGestureViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/OnTapGestureViewModifier.swift @@ -18,7 +18,7 @@ import SwiftUI struct OnTapGestureViewModifier: SwiftUI.ViewModifier { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var modifier: JudoDocument.OnTapGestureModifier @@ -35,7 +35,7 @@ struct OnTapGestureViewModifier: SwiftUI.ViewModifier { private var count: Int { // Count must always be positive. let count = Int(modifier.count.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data )) return count > 0 ? count : 1 @@ -49,7 +49,7 @@ private struct OnTapGesture_iOS15_Modifier: SwiftUI.ViewModifier { @Environment(\.dismiss) private var dismiss // Only available in iOS 15+ @Environment(\.refresh) private var refresh // Only available in iOS 15+ @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let actions: [Action] let count: Int @@ -59,7 +59,7 @@ private struct OnTapGesture_iOS15_Modifier: SwiftUI.ViewModifier { .onTapGesture(count: count) { Actions.perform( actions: actions, - componentState: componentState, + componentBindings: componentBindings, data: data, actionHandlers: actionHandlers ) { @@ -80,7 +80,7 @@ private struct OnTapGesture_Legacy_Modifier: SwiftUI.ViewModifier { @Environment(\.actionHandlers) private var actionHandlers @Environment(\.presentationMode) private var presentationMode @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let actions: [Action] let count: Int @@ -90,7 +90,7 @@ private struct OnTapGesture_Legacy_Modifier: SwiftUI.ViewModifier { .onTapGesture(count: count) { Actions.perform( actions: actions, - componentState: componentState, + componentBindings: componentBindings, data: data, actionHandlers: actionHandlers ) { diff --git a/Sources/JudoRenderer/Modifiers/OpacityViewModifier.swift b/Sources/JudoRenderer/Modifiers/OpacityViewModifier.swift index 9d61164..f30e542 100644 --- a/Sources/JudoRenderer/Modifiers/OpacityViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/OpacityViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct OpacityViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: JudoDocument.OpacityModifier @@ -29,7 +29,7 @@ struct OpacityViewModifier: SwiftUI.ViewModifier { private var opacityValue: Double { modifier.opacity.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/PaddingViewModifier.swift b/Sources/JudoRenderer/Modifiers/PaddingViewModifier.swift index 1041ee9..b953adc 100644 --- a/Sources/JudoRenderer/Modifiers/PaddingViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/PaddingViewModifier.swift @@ -18,7 +18,7 @@ import SwiftUI struct PaddingViewModifier: SwiftUI.ViewModifier { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var modifier: JudoDocument.PaddingModifier @@ -57,7 +57,7 @@ struct PaddingViewModifier: SwiftUI.ViewModifier { private var length: CGFloat? { let resolvedValue = modifier.length?.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -74,19 +74,19 @@ struct PaddingViewModifier: SwiftUI.ViewModifier { return EdgeInsets( top: top.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ), leading: leading.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ), bottom: bottom.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ), trailing: trailing.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) ) diff --git a/Sources/JudoRenderer/Modifiers/PositionModifier.swift b/Sources/JudoRenderer/Modifiers/PositionModifier.swift index f9ba555..063de47 100644 --- a/Sources/JudoRenderer/Modifiers/PositionModifier.swift +++ b/Sources/JudoRenderer/Modifiers/PositionModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct PositionViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: PositionModifier @@ -28,11 +28,11 @@ struct PositionViewModifier: SwiftUI.ViewModifier { } private var x: CGFloat { - modifier.x.forceResolve(propertyValues: componentState.propertyValues, data: data) + modifier.x.forceResolve(propertyValues: componentBindings.propertyValues, data: data) } private var y: CGFloat { - modifier.y.forceResolve(propertyValues: componentState.propertyValues, data: data) + modifier.y.forceResolve(propertyValues: componentBindings.propertyValues, data: data) } } diff --git a/Sources/JudoRenderer/Modifiers/PresentationBackgroundInteractionViewModifier.swift b/Sources/JudoRenderer/Modifiers/PresentationBackgroundInteractionViewModifier.swift index db20d6d..3596856 100644 --- a/Sources/JudoRenderer/Modifiers/PresentationBackgroundInteractionViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/PresentationBackgroundInteractionViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct PresentationBackgroundInteractionViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: PresentationBackgroundInteractionModifier @@ -53,13 +53,13 @@ struct PresentationBackgroundInteractionViewModifier: SwiftUI.ViewModifier { } if let fractionValue = detent.fractionValue { - let resolvedValue = fractionValue.forceResolve(propertyValues: componentState.propertyValues, data: data) + let resolvedValue = fractionValue.forceResolve(propertyValues: componentBindings.propertyValues, data: data) return .enabled(upThrough: .fraction(resolvedValue)) } if let heightValue = detent.heightValue { - let resolvedValue = heightValue.forceResolve(propertyValues: componentState.propertyValues, data: data) + let resolvedValue = heightValue.forceResolve(propertyValues: componentBindings.propertyValues, data: data) return .enabled(upThrough: .height(resolvedValue)) } diff --git a/Sources/JudoRenderer/Modifiers/PresentationCornerRadiusViewModifier.swift b/Sources/JudoRenderer/Modifiers/PresentationCornerRadiusViewModifier.swift index e73cd3b..a82133f 100644 --- a/Sources/JudoRenderer/Modifiers/PresentationCornerRadiusViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/PresentationCornerRadiusViewModifier.swift @@ -18,7 +18,7 @@ import SwiftUI struct PresentationCornerRadiusViewModifier: SwiftUI.ViewModifier { @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var modifier: PresentationCornerRadiusModifier @@ -32,7 +32,7 @@ struct PresentationCornerRadiusViewModifier: SwiftUI.ViewModifier { } var radius: CGFloat? { - let resolvedValue = modifier.radius?.forceResolve(propertyValues: componentState.propertyValues, data: data) + let resolvedValue = modifier.radius?.forceResolve(propertyValues: componentBindings.propertyValues, data: data) return resolvedValue.map { CGFloat($0 )} } diff --git a/Sources/JudoRenderer/Modifiers/RotationEffectViewModifier.swift b/Sources/JudoRenderer/Modifiers/RotationEffectViewModifier.swift index 9e5ab9a..e0f1962 100644 --- a/Sources/JudoRenderer/Modifiers/RotationEffectViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/RotationEffectViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct RotationEffectViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: JudoDocument.RotationEffectModifier @@ -37,7 +37,7 @@ struct RotationEffectViewModifier: SwiftUI.ViewModifier { } private var angleValue: Double { - modifier.angleSize.forceResolve(propertyValues: componentState.propertyValues, data: data) + modifier.angleSize.forceResolve(propertyValues: componentBindings.propertyValues, data: data) } private var anchor: SwiftUI.UnitPoint { diff --git a/Sources/JudoRenderer/Modifiers/ScrollTargetLayoutViewModifier.swift b/Sources/JudoRenderer/Modifiers/ScrollTargetLayoutViewModifier.swift index 3349b23..e06f06b 100644 --- a/Sources/JudoRenderer/Modifiers/ScrollTargetLayoutViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/ScrollTargetLayoutViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct ScrollTargetLayoutViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: ScrollTargetLayoutModifier @@ -33,7 +33,7 @@ struct ScrollTargetLayoutViewModifier: SwiftUI.ViewModifier { private var isEnabled: Bool { modifier.isEnabled.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/ShadowViewModifier.swift b/Sources/JudoRenderer/Modifiers/ShadowViewModifier.swift index 67796e0..7fd3f5a 100644 --- a/Sources/JudoRenderer/Modifiers/ShadowViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/ShadowViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct ShadowViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: JudoDocument.ShadowModifier @@ -37,11 +37,11 @@ struct ShadowViewModifier: SwiftUI.ViewModifier { private var offset: CGSize { CGSize( width: modifier.offsetWidth.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ), height: modifier.offsetHeight.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) ) @@ -49,7 +49,7 @@ struct ShadowViewModifier: SwiftUI.ViewModifier { private var radius: CGFloat { modifier.radius.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/SheetModifier.swift b/Sources/JudoRenderer/Modifiers/SheetModifier.swift index 1407e2a..34e75e2 100644 --- a/Sources/JudoRenderer/Modifiers/SheetModifier.swift +++ b/Sources/JudoRenderer/Modifiers/SheetModifier.swift @@ -38,14 +38,14 @@ private struct Sheet_iOS15_Modifier: ViewModifier { @Environment(\.dismiss) private var dismiss // Only available in iOS 15+ @Environment(\.refresh) private var refresh // Only available in iOS 15+ @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var modifier: SheetModifier func body(content: Content) -> some View { content .sheet(isPresented: isPresented, onDismiss: { - Actions.perform(actions: modifier.onDismissActions, componentState: componentState, data: data, actionHandlers: actionHandlers) { + Actions.perform(actions: modifier.onDismissActions, componentBindings: componentBindings, data: data, actionHandlers: actionHandlers) { dismiss() } openURL: { url in openURL(url) @@ -75,12 +75,12 @@ private struct Sheet_iOS15_Modifier: ViewModifier { private var isPresented: Binding { Binding { modifier.isPresented.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } set: { newValue in if case .property(let name) = modifier.isPresented.binding { - componentState.bindings[name]?.value = .boolean(newValue) + componentBindings[name]?.wrappedValue = newValue } } } @@ -90,7 +90,7 @@ private struct Sheet_Legacy_Modifier: ViewModifier { @Environment(\.openURL) private var openURL @Environment(\.actionHandlers) private var actionHandlers @Environment(\.presentationMode) private var presentationMode - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: SheetModifier @@ -99,7 +99,7 @@ private struct Sheet_Legacy_Modifier: ViewModifier { content .sheet(isPresented: isPresented, onDismiss: { Actions.perform( - actions: modifier.onDismissActions, componentState: componentState, data: data, actionHandlers: actionHandlers) { + actions: modifier.onDismissActions, componentBindings: componentBindings, data: data, actionHandlers: actionHandlers) { presentationMode.wrappedValue.dismiss() } openURL: { url in openURL(url) @@ -125,12 +125,12 @@ private struct Sheet_Legacy_Modifier: ViewModifier { private var isPresented: Binding { Binding { modifier.isPresented.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } set: { newValue in if case .property(let name) = modifier.isPresented.binding { - componentState.bindings[name]?.value = .boolean(newValue) + componentBindings[name]?.wrappedValue = newValue } } } diff --git a/Sources/JudoRenderer/Modifiers/TabItemViewModifier.swift b/Sources/JudoRenderer/Modifiers/TabItemViewModifier.swift index 54981bf..23b9b56 100644 --- a/Sources/JudoRenderer/Modifiers/TabItemViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/TabItemViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct TabItemViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings var modifier: TabItemModifier diff --git a/Sources/JudoRenderer/Modifiers/ToolbarItemViewModifier.swift b/Sources/JudoRenderer/Modifiers/ToolbarItemViewModifier.swift index c601158..b4a042c 100644 --- a/Sources/JudoRenderer/Modifiers/ToolbarItemViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/ToolbarItemViewModifier.swift @@ -76,7 +76,7 @@ private struct ModernToolbarItemButtonView: SwiftUI.View { @Environment(\.openURL) private var openURL @Environment(\.actionHandlers) private var actionHandlers @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let modifier: ToolbarItemModifier @@ -96,7 +96,7 @@ private struct ModernToolbarItemButtonView: SwiftUI.View { case let action as JudoDocument.OpenURLAction: let urlString = action.url.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -111,7 +111,7 @@ private struct ModernToolbarItemButtonView: SwiftUI.View { case let action as CustomAction: let identifier = action.identifier.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -120,21 +120,21 @@ private struct ModernToolbarItemButtonView: SwiftUI.View { let parameters: [String: Any] = action.parameters.reduce(into: [:], { partialResult, parameter in if let textValue = parameter.textValue { let resolvedValue = textValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) partialResult[parameter.key] = resolvedValue } else if let numberValue = parameter.numberValue { let resolvedValue = numberValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) partialResult[parameter.key] = resolvedValue } else if let booleanValue = parameter.booleanValue { let resolvedValue = booleanValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -145,8 +145,8 @@ private struct ModernToolbarItemButtonView: SwiftUI.View { actionHandlers[name]?(parameters) case let action as PropertyAction: - processPropertyAction(action, componentState: componentState, data: data) - + processPropertyAction(action, componentBindings: componentBindings, data: data) + default: assertionFailure("Unexpected action") } @@ -159,7 +159,7 @@ private struct ToolbarItemButtonView: SwiftUI.View { @Environment(\.openURL) private var openURL @Environment(\.actionHandlers) private var actionHandlers @Environment(\.data) private var data - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings let modifier: ToolbarItemModifier @@ -179,7 +179,7 @@ private struct ToolbarItemButtonView: SwiftUI.View { case let action as JudoDocument.OpenURLAction: let urlString = action.url.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -193,7 +193,7 @@ private struct ToolbarItemButtonView: SwiftUI.View { break case let action as CustomAction: - guard let identifier = action.identifier.resolve(propertyValues: componentState.propertyValues, data: data) else { + guard let identifier = action.identifier.resolve(propertyValues: componentBindings.propertyValues, data: data) else { continue } @@ -202,21 +202,21 @@ private struct ToolbarItemButtonView: SwiftUI.View { let parameters: [String: Any] = action.parameters.reduce(into: [:], { partialResult, parameter in if let textValue = parameter.textValue { let resolvedValue = textValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) partialResult[parameter.key] = resolvedValue } else if let numberValue = parameter.numberValue { let resolvedValue = numberValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) partialResult[parameter.key] = resolvedValue } else if let booleanValue = parameter.booleanValue { let resolvedValue = booleanValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -227,8 +227,8 @@ private struct ToolbarItemButtonView: SwiftUI.View { actionHandlers[name]?(parameters) case let action as PropertyAction: - processPropertyAction(action, componentState: componentState, data: data) - + processPropertyAction(action, componentBindings: componentBindings, data: data) + default: assertionFailure("Unexpected action") } @@ -236,7 +236,7 @@ private struct ToolbarItemButtonView: SwiftUI.View { } } -private func processPropertyAction(_ action: JudoDocument.PropertyAction, componentState: ComponentState, data: Any?) { +private func processPropertyAction(_ action: JudoDocument.PropertyAction, componentBindings: ComponentBindings, data: Any?) { guard let propertyName = action.propertyName else { return } @@ -245,63 +245,64 @@ private func processPropertyAction(_ action: JudoDocument.PropertyAction, compon case let action as SetPropertyAction: if let textValue = action.textValue { let resolvedValue = textValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .text(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } else if let numberValue = action.numberValue { let resolvedValue = numberValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .number(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } else if let booleanValue = action.booleanValue { let resolvedValue = booleanValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .boolean(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } else if let imageValue = action.imageValue { let resolvedValue = imageValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .image(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } - case is TogglePropertyAction: - if case .boolean(let value) = componentState.bindings[propertyName]?.value { - componentState.bindings[propertyName]?.value = .boolean(!value) - } else { + guard let value = componentBindings[propertyName]?.wrappedValue as? Bool else { assertionFailure("Unexpected binding type") + return } - + + componentBindings[propertyName]?.wrappedValue = !value case let action as IncrementPropertyAction: - if case .number(let value) = componentState.bindings[propertyName]?.value { - let resolvedByValue = action.value.forceResolve( - propertyValues: componentState.propertyValues, - data: data - ) - - componentState.bindings[propertyName]?.value = .number(value + resolvedByValue) - } else { + guard let value = componentBindings[propertyName]?.wrappedValue as? Double else { assertionFailure("Unexpected binding") + return } + + let resolvedByValue = action.value.forceResolve( + propertyValues: componentBindings.propertyValues, + data: data + ) + + componentBindings[propertyName]?.wrappedValue = value + resolvedByValue case let action as DecrementPropertyAction: - if case .number(let value) = componentState.bindings[propertyName]?.value { - let resolvedByValue = action.value.forceResolve( - propertyValues: componentState.propertyValues, - data: data - ) - - componentState.bindings[propertyName]?.value = .number(value - resolvedByValue) - } else { + guard let value = componentBindings[propertyName]?.wrappedValue as? Double else { assertionFailure("Unexpected binding") + return } + + let resolvedByValue = action.value.forceResolve( + propertyValues: componentBindings.propertyValues, + data: data + ) + + componentBindings[propertyName]?.wrappedValue = value - resolvedByValue default: assertionFailure("Unexpected action") } diff --git a/Sources/JudoRenderer/Modifiers/TrackingViewModifier.swift b/Sources/JudoRenderer/Modifiers/TrackingViewModifier.swift index 671c97f..87a92a5 100644 --- a/Sources/JudoRenderer/Modifiers/TrackingViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/TrackingViewModifier.swift @@ -18,7 +18,7 @@ import SwiftUI struct TrackingViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: JudoDocument.TrackingModifier @@ -30,7 +30,7 @@ struct TrackingViewModifier: SwiftUI.ViewModifier { private var trackingValue: CGFloat { modifier.tracking.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Modifiers/UnderlineViewModifier.swift b/Sources/JudoRenderer/Modifiers/UnderlineViewModifier.swift index 8a8eb7f..a6749f4 100644 --- a/Sources/JudoRenderer/Modifiers/UnderlineViewModifier.swift +++ b/Sources/JudoRenderer/Modifiers/UnderlineViewModifier.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct UnderlineViewModifier: SwiftUI.ViewModifier { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data var modifier: UnderlineModifier @@ -35,7 +35,7 @@ struct UnderlineViewModifier: SwiftUI.ViewModifier { private var isActive: Bool { modifier.isActive.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) } diff --git a/Sources/JudoRenderer/Utils/Actions.swift b/Sources/JudoRenderer/Utils/Actions.swift index 1fdae31..33bf944 100644 --- a/Sources/JudoRenderer/Utils/Actions.swift +++ b/Sources/JudoRenderer/Utils/Actions.swift @@ -20,8 +20,8 @@ enum Actions { @available(iOS 15.0, *) static func perform( actions: [Action], - componentState: ComponentState, - data: Any?, + componentBindings: ComponentBindings, + data: Any?, actionHandlers: [ActionName: ActionHandler], dismiss: (() -> Void)?, openURL: ((URL) -> Void)?, @@ -34,7 +34,7 @@ enum Actions { case let action as JudoDocument.OpenURLAction: let urlString = action.url.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -47,7 +47,7 @@ enum Actions { case let action as CustomAction: let identifier = action.identifier.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -56,21 +56,21 @@ enum Actions { let parameters: [String: Any] = action.parameters.reduce(into: [:], { partialResult, parameter in if let textValue = parameter.textValue { let resolvedValue = textValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) partialResult[parameter.key] = resolvedValue } else if let numberValue = parameter.numberValue { let resolvedValue = numberValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) partialResult[parameter.key] = resolvedValue } else if let booleanValue = parameter.booleanValue { let resolvedValue = booleanValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -85,61 +85,64 @@ enum Actions { case let action as SetPropertyAction: if let textValue = action.textValue { let resolvedValue = textValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .text(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } else if let numberValue = action.numberValue { let resolvedValue = numberValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .number(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } else if let booleanValue = action.booleanValue { let resolvedValue = booleanValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .boolean(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } else if let imageValue = action.imageValue { let resolvedValue = imageValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .image(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } case is TogglePropertyAction: - if case .boolean(let value) = componentState.bindings[propertyName]?.value { - componentState.bindings[propertyName]?.value = .boolean(!value) - } else { + guard let value = componentBindings[propertyName]?.wrappedValue as? Bool else { assertionFailure("Unexpected binding type") + return } - case let action as IncrementPropertyAction: - if case .number(let value) = componentState.bindings[propertyName]?.value { - let resolvedByValue = action.value.forceResolve( - propertyValues: componentState.propertyValues, - data: data - ) - componentState.bindings[propertyName]?.value = .number(value + resolvedByValue) - } else { + componentBindings[propertyName]?.wrappedValue = !value + case let action as IncrementPropertyAction: + guard let value = componentBindings[propertyName]?.wrappedValue as? Double else { assertionFailure("Unexpected binding") + return } - case let action as DecrementPropertyAction: - if case .number(let value) = componentState.bindings[propertyName]?.value { - let resolvedByValue = action.value.forceResolve( - propertyValues: componentState.propertyValues, - data: data - ) - componentState.bindings[propertyName]?.value = .number(value - resolvedByValue) - } else { + let resolvedByValue = action.value.forceResolve( + propertyValues: componentBindings.propertyValues, + data: data + ) + + componentBindings[propertyName]?.wrappedValue = value + resolvedByValue + case let action as DecrementPropertyAction: + guard let value = componentBindings[propertyName]?.wrappedValue as? Double else { assertionFailure("Unexpected binding") + return } + + let resolvedByValue = action.value.forceResolve( + propertyValues: componentBindings.propertyValues, + data: data + ) + + componentBindings[propertyName]?.wrappedValue = value - resolvedByValue default: assertionFailure("Unexpected action") } @@ -152,8 +155,8 @@ enum Actions { static func perform( actions: [Action], - componentState: ComponentState, - data: Any?, + componentBindings: ComponentBindings, + data: Any?, actionHandlers: [ActionName: ActionHandler], dismiss: (() -> Void)?, openURL: ((URL) -> Void)? @@ -165,7 +168,7 @@ enum Actions { case let action as JudoDocument.OpenURLAction: let urlString = action.url.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -180,7 +183,7 @@ enum Actions { case let action as CustomAction: let identifier = action.identifier.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -189,21 +192,21 @@ enum Actions { let parameters: [String: Any] = action.parameters.reduce(into: [:], { partialResult, parameter in if let textValue = parameter.textValue { let resolvedValue = textValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) partialResult[parameter.key] = resolvedValue } else if let numberValue = parameter.numberValue { let resolvedValue = numberValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) partialResult[parameter.key] = resolvedValue } else if let booleanValue = parameter.booleanValue { let resolvedValue = booleanValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) @@ -219,61 +222,64 @@ enum Actions { case let action as SetPropertyAction: if let textValue = action.textValue { let resolvedValue = textValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .text(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } else if let numberValue = action.numberValue { let resolvedValue = numberValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .number(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } else if let booleanValue = action.booleanValue { let resolvedValue = booleanValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .boolean(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } else if let imageValue = action.imageValue { let resolvedValue = imageValue.forceResolve( - propertyValues: componentState.propertyValues, + propertyValues: componentBindings.propertyValues, data: data ) - componentState.bindings[propertyName]?.value = .image(resolvedValue) + componentBindings[propertyName]?.wrappedValue = resolvedValue } case is TogglePropertyAction: - if case .boolean(let value) = componentState.bindings[propertyName]?.value { - componentState.bindings[propertyName]?.value = .boolean(!value) - } else { + guard let value = componentBindings[propertyName]?.wrappedValue as? Bool else { assertionFailure("Unexpected binding type") + return } - case let action as IncrementPropertyAction: - if case .number(let value) = componentState.bindings[propertyName]?.value { - let resolvedByValue = action.value.forceResolve( - propertyValues: componentState.propertyValues, - data: data - ) - componentState.bindings[propertyName]?.value = .number(value + resolvedByValue) - } else { + componentBindings[propertyName]?.wrappedValue = !value + case let action as IncrementPropertyAction: + guard let value = componentBindings[propertyName]?.wrappedValue as? Double else { assertionFailure("Unexpected binding") + return } - case let action as DecrementPropertyAction: - if case .number(let value) = componentState.bindings[propertyName]?.value { - let resolvedByValue = action.value.forceResolve( - propertyValues: componentState.propertyValues, - data: data - ) - componentState.bindings[propertyName]?.value = .number(value - resolvedByValue) - } else { + let resolvedByValue = action.value.forceResolve( + propertyValues: componentBindings.propertyValues, + data: data + ) + + componentBindings[propertyName]?.wrappedValue = value + resolvedByValue + case let action as DecrementPropertyAction: + guard let value = componentBindings[propertyName]?.wrappedValue as? Double else { assertionFailure("Unexpected binding") + return } + + let resolvedByValue = action.value.forceResolve( + propertyValues: componentBindings.propertyValues, + data: data + ) + + componentBindings[propertyName]?.wrappedValue = value - resolvedByValue default: assertionFailure("Unexpected action") } diff --git a/Sources/JudoRenderer/Utils/RealizeText.swift b/Sources/JudoRenderer/Utils/RealizeText.swift index edc1cb9..a097a74 100644 --- a/Sources/JudoRenderer/Utils/RealizeText.swift +++ b/Sources/JudoRenderer/Utils/RealizeText.swift @@ -17,7 +17,7 @@ import JudoDocument import SwiftUI struct RealizeText: SwiftUI.View where Content: SwiftUI.View { - @EnvironmentObject private var componentState: ComponentState + @Environment(\.componentBindings) private var componentBindings @Environment(\.data) private var data @Environment(\.document) private var document @@ -33,7 +33,7 @@ struct RealizeText: SwiftUI.View where Content: SwiftUI.View { var body: some View { content( - realizeText(value, localized: localized, strings: document.strings, propertyValues: componentState.propertyValues, data: data) + realizeText(value, localized: localized, strings: document.strings, propertyValues: componentBindings.propertyValues, data: data) ) } } diff --git a/Tests/ExpressionsTests/InterpreterTests.swift b/Tests/ExpressionsTests/InterpreterTests.swift index 967c3ed..dca03ff 100644 --- a/Tests/ExpressionsTests/InterpreterTests.swift +++ b/Tests/ExpressionsTests/InterpreterTests.swift @@ -4,7 +4,7 @@ import JudoExpressions final class InterpreterTests: XCTestCase { func testInterpreter() throws { - let interpreter = Interpreter() + let interpreter = JudoInterpreter() try XCTAssertEqual(interpreter.interpret("(3 + 3) + (2 * 4)") as? Double, 14.0) try XCTAssertEqual(interpreter.interpret("(3 + 3) + (-2 * 4)") as? Double, -2.0) try XCTAssertEqual(interpreter.interpret("1") as? Double, 1.0) @@ -32,7 +32,7 @@ final class InterpreterTests: XCTestCase { } func testInterpreterInterpolation() throws { - let interpreter = Interpreter() + let interpreter = JudoInterpreter() try XCTAssertEqual(interpreter.interpret("\"1+\\(1 + 2)\"") as? String, "1+3") try XCTAssertEqual(interpreter.interpret("\"1\\(1 + 2)\"") as? String, "13") try XCTAssertEqual(interpreter.interpret("1+\"1\\(1 + 2)\"") as? String, "113") // "1" + "13" @@ -45,25 +45,25 @@ final class InterpreterTests: XCTestCase { func testIdentifier() throws { let variables = [ - ExpressionVariable("a", value: 2.0), - ExpressionVariable("padding", value: 4.0) + JudoExpressionVariable("a", value: 2.0), + JudoExpressionVariable("padding", value: 4.0) ] - let interpreter = Interpreter(variables: variables) + let interpreter = JudoInterpreter(variables: variables) try XCTAssertEqual(interpreter.interpret("2 + a") as? Double, 4.0) try XCTAssertEqual(interpreter.interpret("16.0 + padding") as? Double, 20.0) } func testMethodCall() throws { let variables = [ - ExpressionVariable("a", value: 2.0), - ExpressionVariable("b", value: 5.0), + JudoExpressionVariable("a", value: 2.0), + JudoExpressionVariable("b", value: 5.0), ] let functions = [ // Double.increment(Double) - ExpressionFunction("increment") { caller, arguments in + JudoExpressionFunction("increment") { caller, arguments in guard arguments.count == 1, let value = caller as? Double, let incrementBy = arguments[0] as? Double @@ -76,7 +76,7 @@ final class InterpreterTests: XCTestCase { }, // Double.toString() - ExpressionFunction("toString") { caller, arguments in + JudoExpressionFunction("toString") { caller, arguments in if let value = caller as? Double { return "\(value)" } @@ -86,7 +86,7 @@ final class InterpreterTests: XCTestCase { }, // Boolean.toggle() - ExpressionFunction("toggle") { caller, arguments in + JudoExpressionFunction("toggle") { caller, arguments in guard let value = caller as? Bool else { return caller } @@ -95,7 +95,7 @@ final class InterpreterTests: XCTestCase { } ] - let interpreter = Interpreter( + let interpreter = JudoInterpreter( variables: variables, functions: functions ) @@ -118,19 +118,19 @@ final class InterpreterTests: XCTestCase { func testInterpreterQuote() throws { let variables = [ - ExpressionVariable("a", value: 2.0) + JudoExpressionVariable("a", value: 2.0) ] - let interpreter = Interpreter(variables: variables) + let interpreter = JudoInterpreter(variables: variables) let result = try interpreter.interpret(#""\"foo\(a)bar\"""#) as? String // "\"foo\(a)bar\"" XCTAssertEqual(result, "\"foo2bar\"") } func testStringAddInt() throws { let variables = [ - ExpressionVariable("a", value: "A"), - ExpressionVariable("b", value: 1) + JudoExpressionVariable("a", value: "A"), + JudoExpressionVariable("b", value: 1) ] - let interpreter = Interpreter(variables: variables) + let interpreter = JudoInterpreter(variables: variables) let result = try interpreter.interpret(#"a + b"#) as? String XCTAssertEqual(result, #"A1"#) }