Skip to content

Commit

Permalink
ViewWithStore deprecations and docs (#57)
Browse files Browse the repository at this point in the history
* ViewWithStore migration docs

* StoreView implementation

* deprecation notes

* deprecation notes
  • Loading branch information
KazaiMazai authored Aug 31, 2024
1 parent ef90d7b commit c251958
Show file tree
Hide file tree
Showing 11 changed files with 726 additions and 390 deletions.
2 changes: 1 addition & 1 deletion Sources/Puredux/Store/Deprecated/RootStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public final class RootStore<State, Action> {
/// RootStore is a factory for light-weight stores that are created as proxies for the internal store.
///
///
@available(*, deprecated, message: "Will be removed in the next major release. Consider migrating to StoreFactory")
@available(*, deprecated, message: "Will be removed in 2.0. Consider migrating to StoreFactory")
public init(queue: StoreQueue = .global(qos: .userInteractive),
initialState: State,
reducer: @escaping Reducer<State, Action>) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Puredux/Store/StateStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

@available(*, deprecated, renamed: "StateStore", message: "Will be removed in the next major release. StateStore is a former StoreObject replacement")
@available(*, deprecated, renamed: "StateStore", message: "Will be removed in 2.0. StateStore is a former StoreObject replacement")
public typealias StoreObject = StateStore
/**
A generic state store for managing state and handling actions within a store.
Expand Down
2 changes: 1 addition & 1 deletion Sources/Puredux/Store/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public extension Store {
/// All dispatched Actions and subscribtions are forwarded to the root store.
/// Store is thread safe. Actions can be dispatched from any thread. Can be subscribed from any thread.
///
@available(*, deprecated, message: "Will be removed in the next major release. Consider migrating to scope(...)")
@available(*, deprecated, message: "Will be removed in 2.0. Consider migrating to scope(...)")
func proxy<LocalState>(_ toLocalState: @escaping (State) -> LocalState) -> Store<LocalState, Action> {
map(toLocalState)
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/Puredux/SwiftUI/Deprecated/RootEnvStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public final class RootEnvStore<AppState, Action>: ObservableObject {
/// `RootEnvStore` and `PublishingStore` are SwiftUI-friendly counterparts for Puredux's RootStore and Store.
/// RootEnvStore is what we have on top. PublishingStore are lightweight proxies that we are to connect our views to.
///
@available(*, deprecated, message: "Will be removed in the next major release. Use StoreFactory with EnvStoreFactory instead")
@available(*, deprecated, message: "Will be removed in 2.0. Use StoreFactory with EnvStoreFactory instead")
public init(rootStore: RootStore<AppState, Action>) {
self.rootStore = rootStore
self.stateSubject = PassthroughSubject<AppState, Never>()
Expand All @@ -47,7 +47,7 @@ public extension RootEnvStore {
/// **Important to note:** Proxy store only keeps weak reference to the internal root store,
/// ensuring that reference cycles will not be created.
///
@available(*, deprecated, message: "Will be removed in the next major release. Use EnvStoreFactory's rootStore instead")
@available(*, deprecated, message: "Will be removed in 2.0. Use EnvStoreFactory's rootStore instead")
func store() -> PublishingStore<AppState, Action> {

PublishingStore(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public struct StoreProvidingView<AppState, Aciton, Content: View>: View {
/// )
///
/// ```
@available(*, deprecated, message: "Will be removed in the next major release. Use EnvStoreFactory and ViewWithStoreFactory instead")
@available(*, deprecated, message: "Will be removed in 2.0. Use EnvStoreFactory and ViewWithStoreFactory instead")
public init(rootStore: RootEnvStore<AppState, Aciton>,
content: @escaping () -> Content) {
self.rootStore = rootStore
Expand Down
4 changes: 2 additions & 2 deletions Sources/Puredux/SwiftUI/Deprecated/View+Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ extension View {
/// )
///
/// ```
@available(*, deprecated, message: "Will be removed in the next major release. Use ViewWithStore instead")
@available(*, deprecated, message: "Will be removed in 2.0. Use ViewWithStore instead")
public static func withEnvStore<AppState, Action, Props>(
removeStateDuplicates by: Equating<AppState> = .neverEqual,
props: @escaping (AppState, PublishingStore<AppState, Action>) -> Props,
Expand Down Expand Up @@ -90,7 +90,7 @@ extension View {
/// )
///
/// ```
@available(*, deprecated, message: "Will be removed in the next major release. Use ViewWithStore instead")
@available(*, deprecated, message: "Will be removed in 2.0. Use ViewWithStore instead")
public static func with<AppState, Action, Props>(
store: PublishingStore<AppState, Action>,
removeStateDuplicates by: Equating<AppState> = .neverEqual,
Expand Down
2 changes: 1 addition & 1 deletion Sources/Puredux/SwiftUI/Store/PublishingStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public extension PublishingStore {
/// All dispatched Actions are forwarded to the root store.
/// PublishingStore is thread safe. Actions can be safely dispatched from any thread.
///
@available(*, deprecated, message: "Will be removed in the next major release. Use scope(...) method instead")
@available(*, deprecated, message: "Will be removed in 2.0. Use scope(...) method instead")
func proxy<LocalState>(
toLocalState: @escaping (AppState) -> LocalState) -> PublishingStore<LocalState, Action> {

Expand Down
2 changes: 1 addition & 1 deletion Sources/Puredux/SwiftUI/Store/PublishingStoreObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public struct PublishingStoreObject<AppState, Action> {
/// For all other cases, EnvStoreFactory's methods should be used to create viable PublishingStoreObject.
///
///
@available(*, deprecated, renamed: "init(stateStore:)", message: "Will be removed in the next major release.")
@available(*, deprecated, renamed: "init(stateStore:)", message: "Will be removed in 2.0.")
public init(storeObject: StoreObject<AppState, Action>) {
self.stateStore = storeObject
self.stateSubject = PassthroughSubject<AppState, Never>()
Expand Down
154 changes: 154 additions & 0 deletions Sources/Puredux/SwiftUI/StoreView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//
// File.swift
//
//
// Created by Sergey Kazakov on 31/08/2024.
//

import SwiftUI

/**
A SwiftUI view that binds to a `Store` object to observe its state and dispatch actions.
- Note: `StoreView` is intended to make the migration from `ViewWithStore` to Puredux 2.0 process easier.
Otherwise consider using `view.subscribe(...)` API directly
- Parameters:
- ViewState: The type representing the state of the view.
- Action: The type representing the actions that can be dispatched to modify the state.
- Props: The type representing the properties derived from the state to be passed to the content view.
- Content: A view builder that generates the content for the view based on the provided `Props`.
*/
public struct StoreView<ViewState, Action, Props, Content: View>: View {
private let store: Store<ViewState, Action>
private let props: (_ state: ViewState, _ store: Store<ViewState, Action>) -> Props
private let content: (_ props: Props) -> Content
private(set) var presentationSettings: PresentationSettings<ViewState> = .default

@State private var currentProps: Props?

public var body: some View {
makeContent()
.subscribe(
store: store,
props: props,
presentationQueue: presentationSettings.queue.dispatchQueue,
removeStateDuplicates: .init(predicate: presentationSettings.removeDuplicates)) {
currentProps = $0
}
}

@ViewBuilder
private func makeContent() -> some View {
switch currentProps {
case .none:
Color.clear
case .some(let value):
content(value)
}
}
}

public extension StoreView {
/**
Returns a copy of the `StoreView` with a custom predicate for removing state duplicates.
- Parameter equating: An `Equating` object that defines the predicate for determining if two states are considered duplicates.
- Returns: A new instance of `StoreView` with the updated state duplication removal predicate.
*/
func removeStateDuplicates(_ equating: Equating<ViewState>) -> Self {
var selfCopy = self
selfCopy.presentationSettings.removeDuplicates = equating.predicate
return selfCopy
}

/**
Returns a copy of the `StoreView` configured to use a specific `DispatchQueue` for state presentation updates.
- Parameter queue: A `DispatchQueue` on which state updates should be presented.
- Returns: A new instance of `StoreView` with the updated presentation queue.
*/
func usePresentationQueue(_ queue: DispatchQueue) -> Self {
var selfCopy = self
selfCopy.presentationSettings.queue = .serialQueue(queue)
return selfCopy
}

/**
Returns a copy of the `StoreView` configured to use a specific `PresentationQueue` for state presentation updates.
- Parameter queue: A `PresentationQueue` that wraps a `DispatchQueue` for state updates.
- Returns: A new instance of `StoreView` with the updated presentation queue.
*/
func usePresentationQueue(_ queue: PresentationQueue) -> Self {
usePresentationQueue(queue.dispatchQueue)
}
}

public extension StoreView {
/**
Initializes a `StoreView` with a store that conforms to `StoreProtocol`, and provides closures for deriving properties and content.
- Parameters:
- store: An instance of `StoreProtocol` that conforms to `ViewState` and `Action`. The store is converted to a `Store` using `getStore()`.
- props: A closure that takes the current state and store, and returns the properties for the content view.
- content: A closure that takes the derived properties and returns the SwiftUI view to display.
*/
init(_ store: any StoreProtocol<ViewState, Action>,
props: @escaping (ViewState, Store<ViewState, Action>) -> Props,
content: @escaping (Props) -> Content) {
self.store = store.getStore()
self.props = props
self.content = content
}

/**
Initializes a `StoreView` with a store that conforms to `StoreProtocol`, and provides closures for deriving properties and content, where the store dispatch function is directly accessible.
- Parameters:
- store: An instance of `StoreProtocol` that conforms to `ViewState` and `Action`. The store is converted to a `Store` using `getStore()`.
- props: A closure that takes the current state and a dispatch function, and returns the properties for the content view.
- content: A closure that takes the derived properties and returns the SwiftUI view to display.
*/
init(_ store: any StoreProtocol<ViewState, Action>,
props: @escaping (ViewState, @escaping Dispatch<Action>) -> Props,
content: @escaping (Props) -> Content) {
self.store = store.getStore()
self.props = { state, store in props(state, store.dispatch) }
self.content = content
}
}


public extension StoreView where Props == (ViewState, Store<ViewState, Action>) {
/**
Initializes a `StoreView` where `Props` is a tuple of `(ViewState, Store<ViewState, Action>)`.
- Parameters:
- store: An instance of `StoreProtocol` that conforms to `ViewState` and `Action`.
- content: A closure that takes a tuple containing the current state and the store, and returns the SwiftUI view to display.
*/
init(_ store: any StoreProtocol<ViewState, Action>,
content: @escaping (Props) -> Content) {
self.store = store.getStore()
self.props = { state, store in (state, store) }
self.content = content
}
}

public extension StoreView where Props == (ViewState, Store<ViewState, Action>) {
/**
Initializes a `StoreView` where `Props` is a tuple of `(ViewState, Store<ViewState, Action>)`, with a content closure that uses a dispatch function.
- Parameters:
- store: An instance of `StoreProtocol` that conforms to `ViewState` and `Action`.
- content: A closure that takes the current state and a dispatch function, and returns the SwiftUI view to display.
*/
init(_ store: any StoreProtocol<ViewState, Action>,
content: @escaping (ViewState, @escaping Dispatch<Action>) -> Content) {
self.store = store.getStore()
self.props = { state, store in (state, store) }
self.content = { props in content(props.0, props.1.dispatch) }
}
}
Loading

0 comments on commit c251958

Please sign in to comment.