Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose helpers for static models #311

Merged
merged 1 commit into from
Nov 14, 2024
Merged
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
28 changes: 26 additions & 2 deletions WorkflowSwiftUI/Sources/ActionModel.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
/// An ``ObservableModel`` for workflows with a single action.
///
/// Rather than creating this model directly, you should use the
/// ``Workflow/RenderContext/makeActionModel(state:)`` method to create an instance of this model.
/// To create an accessor, use
/// ``Workflow/RenderContext/makeActionModel(state:)``. State writes and actions
/// will be sent to the workflow.
public struct ActionModel<State: ObservableState, Action>: ObservableModel, SingleActionModel {
public let accessor: StateAccessor<State>
public let sendAction: (Action) -> Void

/// Creates a new ActionModel.
///
/// Rather than creating this model directly, you should usually use the
/// ``Workflow/RenderContext/makeActionModel(state:)`` method to create an
/// instance of this model. If you need a static model for testing or
/// previews, you can use the ``constant(state:)`` method.
public init(accessor: StateAccessor<State>, sendAction: @escaping (Action) -> Void) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are there any reasons we'd want to restrict clients' ability to create these types in general?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To reduce the chance of folks using it wrong, mainly. You can't create something like a RenderContext either. The type's docs already hint at this, and I can copy it to the init as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be clear – you think the convenience of exposing the public constructor outweighs the chance of misuse?

self.accessor = accessor
self.sendAction = sendAction
}
}

/// An observable model with a single action.
Expand All @@ -22,3 +34,15 @@ extension ActionModel: Identifiable where State: Identifiable {
accessor.id
}
}

#if DEBUG

public extension ActionModel {
/// Creates a static model which ignores all sent values, suitable for static previews
/// or testing.
static func constant(state: State) -> ActionModel<State, Action> {
ActionModel(accessor: .constant(state: state), sendAction: { _ in })
}
}

#endif
18 changes: 18 additions & 0 deletions WorkflowSwiftUI/Sources/StateAccessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ public struct StateAccessor<State: ObservableState> {
let state: State
let sendValue: (@escaping (inout State) -> Void) -> Void

/// Creates a new state accessor.
///
/// Rather than creating this model directly, you should usually use the
/// ``Workflow/RenderContext/makeStateAccessor(state:)`` method. If you need
/// a static model for testing or previews, you can use the
/// ``constant(state:)`` method.
public init(
state: State,
sendValue: @escaping (@escaping (inout State) -> Void) -> Void
Expand All @@ -31,3 +37,15 @@ extension StateAccessor: Identifiable where State: Identifiable {
state.id
}
}

#if DEBUG

public extension StateAccessor {
/// Creates a static state accessor which ignores all sent values, suitable for static previews
/// or testing.
static func constant(state: State) -> StateAccessor<State> {
StateAccessor(state: state, sendValue: { _ in })
}
}

#endif
22 changes: 8 additions & 14 deletions WorkflowSwiftUI/Sources/Store+Preview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,17 @@ public struct StaticStorePreviewContext {
}

public func makeStateAccessor<State>(state: State) -> StateAccessor<State> {
StateAccessor(
state: state,
sendValue: { _ in }
)
.constant(state: state)
}

public func makeActionModel<State, Action>(
state: State
) -> ActionModel<State, Action> {
ActionModel(
accessor: makeStateAccessor(state: state),
sendAction: makeSink(of: Action.self).send
)
.constant(state: state)
}
}

extension Store {
public extension Store {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep the access control on the individual methods.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but the linter did this!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approve me! #315

/// Generates a static store for previews.
///
/// Previews generated with this method are static and do not update state. To generate a
Expand All @@ -38,15 +32,15 @@ extension Store {
/// - Parameter makeModel: A closure to create the store's model. The provided `context` param
/// is a convenience to generate dummy sinks and state accessors.
/// - Returns: A store for previews.
public static func preview(
static func preview(
makeModel: (StaticStorePreviewContext) -> Model
) -> Store {
let context = StaticStorePreviewContext()
let model = makeModel(context)
let (store, _) = make(model: model)
return store
}

/// Generates a static store for previews.
///
/// Previews generated with this method are static and do not update state. To generate a
Expand All @@ -55,14 +49,14 @@ extension Store {
///
/// - Parameter state: The state of the view.
/// - Returns: A store for previews.
public static func preview<State, Action>(
static func preview<State, Action>(
state: State
) -> Store<ActionModel<State, Action>> where Model == ActionModel<State, Action> {
preview { context in
context.makeActionModel(state: state)
}
}

/// Generates a static store for previews.
///
/// Previews generated with this method are static and do not update state. To generate a
Expand All @@ -71,7 +65,7 @@ extension Store {
///
/// - Parameter state: The state of the view.
/// - Returns: A store for previews.
public static func preview<State>(
static func preview<State>(
state: State
) -> Store<StateAccessor<State>> where Model == StateAccessor<State> {
preview { context in
Expand Down
Loading