diff --git a/Samples/SwiftUITestbed/Sources/MainScreen.swift b/Samples/SwiftUITestbed/Sources/MainScreen.swift index ecbd61049..483feefd6 100644 --- a/Samples/SwiftUITestbed/Sources/MainScreen.swift +++ b/Samples/SwiftUITestbed/Sources/MainScreen.swift @@ -31,7 +31,7 @@ extension MainWorkflow.Rendering: SwiftUIScreen, Screen { } private struct MainScreenView: View { - var model: Store + @BindableStore var model: Store @Environment(\.viewEnvironment.marketStylesheet) private var styles: MarketStylesheet @Environment(\.viewEnvironment.marketContext) private var context: MarketContext @@ -49,10 +49,7 @@ private struct MainScreenView: View { TextField( "Text", - text: Binding( - get: { model.title }, - set: { _ in fatalError("TODO") } - ) + text: $model.title ) .focused($focusedField, equals: .title) .onAppear { focusedField = .title } @@ -61,10 +58,7 @@ private struct MainScreenView: View { style: context.stylesheets.testbed.toggleRow, label: "All Caps", isEnabled: model.allCapsToggleIsEnabled, - isOn: Binding( - get: { model.allCapsToggleIsOn }, - set: { _ in fatalError("TODO") } - ) + isOn: $model.allCapsToggleIsOn ) Spacer(minLength: styles.spacings.spacing50) diff --git a/Samples/SwiftUITestbed/Sources/MainWorkflow.swift b/Samples/SwiftUITestbed/Sources/MainWorkflow.swift index 72ca41a42..308e2ae39 100644 --- a/Samples/SwiftUITestbed/Sources/MainWorkflow.swift +++ b/Samples/SwiftUITestbed/Sources/MainWorkflow.swift @@ -78,7 +78,8 @@ struct MainWorkflow: Workflow { extension MainWorkflow.State { var allCapsToggleIsOn: Bool { - isAllCaps + get { isAllCaps } + set { fatalError("TODO") } } var allCapsToggleIsEnabled: Bool { diff --git a/Samples/SwiftUITestbed/Sources/Observation/BindableStore.swift b/Samples/SwiftUITestbed/Sources/Observation/BindableStore.swift new file mode 100644 index 000000000..77626bea7 --- /dev/null +++ b/Samples/SwiftUITestbed/Sources/Observation/BindableStore.swift @@ -0,0 +1,38 @@ +// Copied from https://github.com/pointfreeco/swift-composable-architecture/blob/acfbab4290adda4e47026d059db36361958d495c/Sources/ComposableArchitecture/Observation/BindableStore.swift + +import ComposableArchitecture +import SwiftUI + +/// A property wrapper type that supports creating bindings to the mutable properties of a +/// ``Store``. +/// +/// Use this property wrapper in iOS 16, macOS 13, tvOS 16, watchOS 9, and earlier, when `@Bindable` +/// is unavailable, to derive bindings to properties of your features. +/// +/// If you are targeting iOS 17, macOS 14, tvOS 17, watchOS 9, or later, then you can replace +/// ``BindableStore`` with SwiftUI's `@Bindable`. +@available(iOS, deprecated: 17, renamed: "Bindable") +@available(macOS, deprecated: 14, renamed: "Bindable") +@available(tvOS, deprecated: 17, renamed: "Bindable") +@available(watchOS, deprecated: 10, renamed: "Bindable") +@propertyWrapper +@dynamicMemberLookup +public struct BindableStore { + public var wrappedValue: Store + public init(wrappedValue: Store) { + self.wrappedValue = wrappedValue + } + + public var projectedValue: BindableStore { + self + } + + public subscript( + dynamicMember keyPath: ReferenceWritableKeyPath, Subject> + ) -> Binding { + Binding( + get: { self.wrappedValue[keyPath: keyPath] }, + set: { self.wrappedValue[keyPath: keyPath] = $0 } + ) + } +} diff --git a/Samples/SwiftUITestbed/Sources/Observation/Store.swift b/Samples/SwiftUITestbed/Sources/Observation/Store.swift index 8b4466798..67a49f3fb 100644 --- a/Samples/SwiftUITestbed/Sources/Observation/Store.swift +++ b/Samples/SwiftUITestbed/Sources/Observation/Store.swift @@ -36,6 +36,13 @@ public extension Store { subscript(dynamicMember keyPath: KeyPath) -> T { state[keyPath: keyPath] } + + subscript( + dynamicMember keyPath: WritableKeyPath + ) -> T { + get { state[keyPath: keyPath] } + set { state[keyPath: keyPath] = newValue } + } } extension Store: Equatable {