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
6 changes: 3 additions & 3 deletions Workflow/Sources/AnyWorkflow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ extension AnyWorkflow {
/// That type information *is* present in our storage object, however, so we
/// pass the context down to that storage object which will ultimately call
/// through to `context.render(workflow:key:reducer:)`.
internal func render<Parent, Action>(context: RenderContext<Parent>, key: String, outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowAction, Action.WorkflowType == Parent {
internal func render<Parent, Action>(context: RenderContext<Parent>, key: String, outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowActionCore, Action.WorkflowType == Parent {
return storage.render(context: context, key: key, outputMap: outputMap)
}
}
Expand All @@ -103,7 +103,7 @@ extension AnyWorkflow {
fileprivate class AnyStorage {
var base: Any { fatalError() }

func render<Parent, Action>(context: RenderContext<Parent>, key: String, outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowAction, Action.WorkflowType == Parent {
func render<Parent, Action>(context: RenderContext<Parent>, key: String, outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowActionCore, Action.WorkflowType == Parent {
fatalError()
}

Expand Down Expand Up @@ -140,7 +140,7 @@ extension AnyWorkflow {
return T.self
}

override func render<Parent, Action>(context: RenderContext<Parent>, key: String, outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowAction, Action.WorkflowType == Parent {
override func render<Parent, Action>(context: RenderContext<Parent>, key: String, outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowActionCore, Action.WorkflowType == Parent {
let outputMap: (T.Output) -> Action = { [outputTransform] output in
outputMap(outputTransform(output))
}
Expand Down
8 changes: 4 additions & 4 deletions Workflow/Sources/AnyWorkflowConvertible.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ extension AnyWorkflowConvertible {
/// - Parameter key: A string that uniquely identifies this workflow.
///
/// - Returns: The `Rendering` generated by the workflow.
public func rendered<Parent>(in context: RenderContext<Parent>, key: String = "") -> Rendering where Output: WorkflowAction, Output.WorkflowType == Parent {
public func rendered<Parent>(in context: RenderContext<Parent>, key: String = "") -> Rendering where Output: WorkflowActionCore, Output.WorkflowType == Parent {
return asAnyWorkflow().render(context: context, key: key, outputMap: { $0 })
}

public func rendered<Parent, Action>(in context: RenderContext<Parent>, key: String = "", outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowAction, Action.WorkflowType == Parent {
public func rendered<Parent, Action>(in context: RenderContext<Parent>, key: String = "", outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowActionCore, Action.WorkflowType == Parent {
return asAnyWorkflow().render(context: context, key: key, outputMap: { outputMap($0) })
}

Expand Down Expand Up @@ -73,12 +73,12 @@ extension AnyWorkflowConvertible where Output == Never {
}

extension AnyWorkflowConvertible where Rendering == Void {
public func running<Parent, Action>(in context: RenderContext<Parent>, key: String = "", outputMap: @escaping (Output) -> Action) where Action: WorkflowAction, Action.WorkflowType == Parent {
public func running<Parent, Action>(in context: RenderContext<Parent>, key: String = "", outputMap: @escaping (Output) -> Action) where Action: WorkflowActionCore, Action.WorkflowType == Parent {
rendered(in: context, key: key, outputMap: outputMap)
}
}

extension AnyWorkflowConvertible where Rendering == Void, Output: WorkflowAction {
extension AnyWorkflowConvertible where Rendering == Void, Output: WorkflowActionCore {
public func running<Parent>(in context: RenderContext<Parent>, key: String = "") where Output.WorkflowType == Parent {
rendered(in: context, key: key)
}
Expand Down
12 changes: 6 additions & 6 deletions Workflow/Sources/RenderContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public class RenderContext<WorkflowType: Workflow>: RenderContextType {
/// - Parameter key: A string that uniquely identifies this child.
///
/// - Returns: The `Rendering` result of the child's `render` method.
func render<Child, Action>(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Child: Workflow, Action: WorkflowAction, WorkflowType == Action.WorkflowType {
func render<Child, Action>(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Child: Workflow, Action: WorkflowActionCore, WorkflowType == Action.WorkflowType {
fatalError()
}

Expand All @@ -77,7 +77,7 @@ public class RenderContext<WorkflowType: Workflow>: RenderContextType {
///
/// - Parameter actionType: The type of Action this Sink may process
/// - Returns: A Sink capable of relaying `Action` instances to the Workflow runtime
public func makeSink<Action>(of actionType: Action.Type) -> Sink<Action> where Action: WorkflowAction, Action.WorkflowType == WorkflowType {
public func makeSink<Action>(of actionType: Action.Type) -> Sink<Action> where Action: WorkflowActionCore, Action.WorkflowType == WorkflowType {
fatalError()
}

Expand Down Expand Up @@ -118,12 +118,12 @@ public class RenderContext<WorkflowType: Workflow>: RenderContextType {
super.init()
}

override func render<Child, Action>(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where WorkflowType == Action.WorkflowType, Child: Workflow, Action: WorkflowAction {
override func render<Child, Action>(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where WorkflowType == Action.WorkflowType, Child: Workflow, Action: WorkflowActionCore {
assertStillValid()
return implementation.render(workflow: workflow, key: key, outputMap: outputMap)
}

override func makeSink<Action>(of actionType: Action.Type) -> Sink<Action> where WorkflowType == Action.WorkflowType, Action: WorkflowAction {
override func makeSink<Action>(of actionType: Action.Type) -> Sink<Action> where WorkflowType == Action.WorkflowType, Action: WorkflowActionCore {
assertStillValid()
return implementation.makeSink(of: actionType)
}
Expand All @@ -142,9 +142,9 @@ public class RenderContext<WorkflowType: Workflow>: RenderContextType {
internal protocol RenderContextType: AnyObject {
associatedtype WorkflowType: Workflow

func render<Child, Action>(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Child: Workflow, Action: WorkflowAction, Action.WorkflowType == WorkflowType
func render<Child, Action>(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Child: Workflow, Action: WorkflowActionCore, Action.WorkflowType == WorkflowType

func makeSink<Action>(of actionType: Action.Type) -> Sink<Action> where Action: WorkflowAction, Action.WorkflowType == WorkflowType
func makeSink<Action>(of actionType: Action.Type) -> Sink<Action> where Action: WorkflowActionCore, Action.WorkflowType == WorkflowType

func runSideEffect(key: AnyHashable, action: (_ lifetime: Lifetime) -> Void)
}
Expand Down
2 changes: 1 addition & 1 deletion Workflow/Sources/Sink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

/// Sink is a type that receives incoming values (commonly events or `WorkflowAction`)
/// Sink is a type that receives incoming values (commonly events or `WorkflowActionCore`)
///
/// Use `RenderContext.makeSink` to create instances.
public struct Sink<Value> {
Expand Down
16 changes: 8 additions & 8 deletions Workflow/Sources/SubtreeManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ extension WorkflowNode {

extension WorkflowNode.SubtreeManager {
enum Output {
case update(any WorkflowAction<WorkflowType>, source: WorkflowUpdateDebugInfo.Source)
case update(any WorkflowActionCore<WorkflowType>, source: WorkflowUpdateDebugInfo.Source)
case childDidUpdate(WorkflowUpdateDebugInfo)
}
}
Expand Down Expand Up @@ -178,7 +178,7 @@ extension WorkflowNode.SubtreeManager {
outputMap: @escaping (Child.Output) -> Action
) -> Child.Rendering
where Child: Workflow,
Action: WorkflowAction,
Action: WorkflowActionCore,
WorkflowType == Action.WorkflowType {
/// A unique key used to identify this child workflow
let childKey = ChildKey(childType: Child.self, key: key)
Expand Down Expand Up @@ -227,7 +227,7 @@ extension WorkflowNode.SubtreeManager {
return child.render()
}

func makeSink<Action>(of actionType: Action.Type) -> Sink<Action> where Action: WorkflowAction, WorkflowType == Action.WorkflowType {
func makeSink<Action>(of actionType: Action.Type) -> Sink<Action> where Action: WorkflowActionCore, WorkflowType == Action.WorkflowType {
let reusableSink = sinkStore.findOrCreate(actionType: Action.self)

let sink = Sink<Action> { [weak reusableSink] action in
Expand Down Expand Up @@ -270,7 +270,7 @@ extension WorkflowNode.SubtreeManager {
self.usedSinks = [:]
}

mutating func findOrCreate<Action: WorkflowAction>(actionType: Action.Type) -> ReusableSink<Action> {
mutating func findOrCreate<Action: WorkflowActionCore>(actionType: Action.Type) -> ReusableSink<Action> {
let key = ObjectIdentifier(actionType)

let reusableSink: ReusableSink<Action>
Expand Down Expand Up @@ -302,7 +302,7 @@ extension WorkflowNode.SubtreeManager {
}
}

fileprivate final class ReusableSink<Action: WorkflowAction>: AnyReusableSink where Action.WorkflowType == WorkflowType {
fileprivate final class ReusableSink<Action: WorkflowActionCore>: AnyReusableSink where Action.WorkflowType == WorkflowType {
func handle(action: Action) {
let output = Output.update(action, source: .external)

Expand Down Expand Up @@ -445,11 +445,11 @@ extension WorkflowNode.SubtreeManager {

fileprivate final class ChildWorkflow<W: Workflow>: AnyChildWorkflow {
private let node: WorkflowNode<W>
private var outputMap: (W.Output) -> any WorkflowAction<WorkflowType>
private var outputMap: (W.Output) -> any WorkflowActionCore<WorkflowType>

init(
workflow: W,
outputMap: @escaping (W.Output) -> any WorkflowAction<WorkflowType>,
outputMap: @escaping (W.Output) -> any WorkflowActionCore<WorkflowType>,
eventPipe: EventPipe,
key: String,
parentSession: WorkflowSession?,
Expand Down Expand Up @@ -480,7 +480,7 @@ extension WorkflowNode.SubtreeManager {

func update(
workflow: W,
outputMap: @escaping (W.Output) -> any WorkflowAction<WorkflowType>,
outputMap: @escaping (W.Output) -> any WorkflowActionCore<WorkflowType>,
eventPipe: EventPipe
) {
self.outputMap = outputMap
Expand Down
57 changes: 42 additions & 15 deletions Workflow/Sources/WorkflowAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@
* limitations under the License.
*/

/// Conforming types represent an action that advances a workflow. When applied, an action emits the next
/// state and / or output for the workflow.
public protocol WorkflowAction<WorkflowType> {
// TODO: document this
public protocol WorkflowActionCore<WorkflowType> {
/// The type of workflow that this action can be applied to.
associatedtype WorkflowType: Workflow

// TODO: document this
func apply(toState state: inout WorkflowType.State, workflow: WorkflowType) -> WorkflowType.Output?
}

/// Conforming types represent an action that advances a workflow. When applied, an action emits the next
/// state and / or output for the workflow.
public protocol WorkflowAction: WorkflowActionCore {
/// Applies this action to a given state of the workflow, optionally returning an output event.
///
/// - Parameter state: The current state of the workflow. The state is passed as an `inout` param, allowing actions
Expand All @@ -30,11 +36,17 @@ public protocol WorkflowAction<WorkflowType> {
func apply(toState state: inout WorkflowType.State) -> WorkflowType.Output?
}

public extension WorkflowActionCore where Self: WorkflowAction {
func apply(toState state: inout WorkflowType.State, workflow: WorkflowType) -> WorkflowType.Output? {
apply(toState: &state)
}
}

/// A type-erased workflow action.
///
/// The `AnyWorkflowAction` type forwards `apply` to an underlying workflow action, hiding its specific underlying type.
public struct AnyWorkflowAction<WorkflowType: Workflow>: WorkflowAction {
private let _apply: (inout WorkflowType.State) -> WorkflowType.Output?
public struct AnyWorkflowAction<WorkflowType: Workflow>: WorkflowActionCore {
private let _apply: (inout WorkflowType.State, WorkflowType) -> WorkflowType.Output?

/// The underlying type-erased `WorkflowAction`
public let base: Any
Expand All @@ -45,12 +57,12 @@ public struct AnyWorkflowAction<WorkflowType: Workflow>: WorkflowAction {
/// Creates a type-erased workflow action that wraps the given instance.
///
/// - Parameter base: A workflow action to wrap.
public init<E>(_ base: E) where E: WorkflowAction, E.WorkflowType == WorkflowType {
public init<E>(_ base: E) where E: WorkflowActionCore, E.WorkflowType == WorkflowType {
if let anyEvent = base as? AnyWorkflowAction<WorkflowType> {
self = anyEvent
return
}
self._apply = { return base.apply(toState: &$0) }
self._apply = base.apply
self.base = base
self.isClosureBased = false
}
Expand All @@ -62,6 +74,21 @@ public struct AnyWorkflowAction<WorkflowType: Workflow>: WorkflowAction {
_ apply: @escaping (inout WorkflowType.State) -> WorkflowType.Output?,
fileID: StaticString = #fileID,
line: UInt = #line
) {
self.init(
{ state, _ in apply(&state) },
fileID: fileID,
line: line
)
}

/// Creates a type-erased workflow action with the given `apply` implementation.
///
/// - Parameter apply: the apply function for the resulting action.
public init(
_ apply: @escaping (inout WorkflowType.State, WorkflowType) -> WorkflowType.Output?,
fileID: StaticString = #fileID,
line: UInt = #line
) {
let closureAction = ClosureAction<WorkflowType>(
_apply: apply,
Expand All @@ -74,13 +101,13 @@ public struct AnyWorkflowAction<WorkflowType: Workflow>: WorkflowAction {
/// Private initializer forwarded to via `init(_ apply:...)`
/// - Parameter closureAction: The `ClosureAction` wrapping the underlying `apply` closure.
fileprivate init(closureAction: ClosureAction<WorkflowType>) {
self._apply = closureAction.apply(toState:)
self._apply = closureAction.apply(toState:workflow:)
self.base = closureAction
self.isClosureBased = true
}

public func apply(toState state: inout WorkflowType.State) -> WorkflowType.Output? {
return _apply(&state)
public func apply(toState state: inout WorkflowType.State, workflow: WorkflowType) -> WorkflowType.Output? {
return _apply(&state, workflow)
}
}

Expand Down Expand Up @@ -108,13 +135,13 @@ extension AnyWorkflowAction {
/// A `WorkflowAction` that wraps an `apply(...)` implementation defined by a closure.
/// Mainly used to provide more useful debugging/telemetry information for `AnyWorkflow` instances
/// defined via a closure.
struct ClosureAction<WorkflowType: Workflow>: WorkflowAction {
private let _apply: (inout WorkflowType.State) -> WorkflowType.Output?
struct ClosureAction<WorkflowType: Workflow>: WorkflowActionCore {
private let _apply: (inout WorkflowType.State, WorkflowType) -> WorkflowType.Output?
let fileID: StaticString
let line: UInt

init(
_apply: @escaping (inout WorkflowType.State) -> WorkflowType.Output?,
_apply: @escaping (inout WorkflowType.State, WorkflowType) -> WorkflowType.Output?,
fileID: StaticString,
line: UInt
) {
Expand All @@ -123,8 +150,8 @@ struct ClosureAction<WorkflowType: Workflow>: WorkflowAction {
self.line = line
}

func apply(toState state: inout WorkflowType.State) -> WorkflowType.Output? {
_apply(&state)
func apply(toState state: inout WorkflowType.State, workflow: WorkflowType) -> WorkflowType.Output? {
_apply(&state, workflow)
}
}

Expand Down
2 changes: 1 addition & 1 deletion Workflow/Sources/WorkflowLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ final class WorkflowLogger {
os_signpost(.end, log: .active, name: "Alive", signpostID: signpostID)
}

static func logSinkEvent<Action: WorkflowAction>(ref: AnyObject, action: Action) {
static func logSinkEvent<Action: WorkflowActionCore>(ref: AnyObject, action: Action) {
guard WorkflowLogging.config.logActions else { return }

let signpostID = OSSignpostID(log: .active, object: ref)
Expand Down
10 changes: 5 additions & 5 deletions Workflow/Sources/WorkflowNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ final class WorkflowNode<WorkflowType: Workflow> {

switch subtreeOutput {
case .update(let action, let source):
/// 'Opens' the existential `any WorkflowAction<WorkflowType>` value
/// 'Opens' the existential `any WorkflowActionCore<WorkflowType>` value
/// allowing the underlying conformance to be applied to the Workflow's State
let outputEvent = openAndApply(
action,
Expand Down Expand Up @@ -178,12 +178,12 @@ extension WorkflowNode {
}

private extension WorkflowNode {
/// Applies an appropriate `WorkflowAction` to advance the underlying Workflow `State`
/// Applies an appropriate `WorkflowActionCore` to advance the underlying Workflow `State`
/// - Parameters:
/// - action: The `WorkflowAction` to apply
/// - action: The `WorkflowActionCore` to apply
/// - isExternal: Whether the handled action came from the 'outside world' vs being bubbled up from a child node
/// - Returns: An optional `Output` produced by the action application
func openAndApply<A: WorkflowAction>(
func openAndApply<A: WorkflowActionCore>(
_ action: A,
isExternal: Bool
) -> WorkflowType.Output? where A.WorkflowType == WorkflowType {
Expand All @@ -208,7 +208,7 @@ private extension WorkflowNode {
defer { observerCompletion?(state, output) }

/// Apply the action to the current state
output = action.apply(toState: &state)
output = action.apply(toState: &state, workflow: workflow)

return output
}
Expand Down
Loading