Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/OneWay/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ where R.Action: Sendable, R.State: Sendable & Equatable {

private let reducer: any Reducer<Action, State>
private let continuation: AsyncStream<State>.Continuation
private var isProcessing: Bool = false
public private(set) var isProcessing: Bool = false
private var actionQueue: [Action] = []
private var bindingTask: Task<Void, Never>?
private var tasks: [TaskID: Task<Void, Never>] = [:]
Expand Down
27 changes: 26 additions & 1 deletion Sources/OneWay/ViewStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ where R.Action: Sendable, R.State: Sendable & Equatable {
public let initialState: State

/// The current state of a store.
public var state: State {
public private(set) var state: State {
didSet {
continuation.yield(state)
states.send(state)
Expand Down Expand Up @@ -97,3 +97,28 @@ where R.Action: Sendable, R.State: Sendable & Equatable {
#if canImport(Combine)
extension ViewStore: ObservableObject { }
#endif


#if canImport(SwiftUI)
import SwiftUI

extension ViewStore {
func binding<T>(_ keyPath: WritableKeyPath<State, T>) -> Binding<T> {
Binding(
get: { self.state[keyPath: keyPath] },
set: { value in
Task {
await self.store.justSetStateValue((value, keyPath))
}
}
)
}
}

extension Store {
fileprivate func justSetStateValue<T>(_ setter: (T, WritableKeyPath<State, T>)) {
guard !isProcessing else { return }
self.state[keyPath: setter.1] = setter.0
}
}
#endif