From 6d80065733bc30446ce58334c850df35c206e073 Mon Sep 17 00:00:00 2001 From: Dave Apgar Date: Wed, 17 Apr 2019 17:40:46 -0700 Subject: [PATCH] Rename `compose` to `render` and update docs. Closes #266, Closes #303 --- docs/concepts.md | 40 +++++++++---------- docs/faq.md | 2 +- docs/swift/building-a-workflow.md | 36 ++++++++--------- docs/swift/using-a-workflow-for-ui.md | 2 +- .../___FILEBASENAME___Workflow.swift | 2 +- swift/Workflow/Sources/AnyWorkflow.swift | 6 +-- .../Sources/AnyWorkflowConvertible.swift | 6 +-- ...kflowContext.swift => RenderContext.swift} | 28 ++++++------- swift/Workflow/Sources/SubtreeManager.swift | 6 +-- swift/Workflow/Sources/Worker.swift | 2 +- swift/Workflow/Sources/Workflow.swift | 16 ++++---- swift/Workflow/Sources/WorkflowNode.swift | 2 +- swift/Workflow/Tests/AnyWorkflowTests.swift | 4 +- .../Workflow/Tests/SubtreeManagerTests.swift | 6 +-- swift/Workflow/Tests/WorkflowNodeTests.swift | 10 ++--- .../Tests/ContainerViewControllerTests.swift | 2 +- 16 files changed, 85 insertions(+), 85 deletions(-) rename swift/Workflow/Sources/{WorkflowContext.swift => RenderContext.swift} (83%) diff --git a/docs/concepts.md b/docs/concepts.md index a06f866c1..1d0ff006e 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -30,31 +30,31 @@ public protocol Workflow: AnyWorkflowConvertible { func workflowDidChange(from previousWorkflow: Self, state: inout State) - func compose(state: State, context: WorkflowContext) -> Rendering + func render(state: State, context: RenderContext) -> Rendering } ``` ```kotlin -interface Workflow { +interface Workflow { - fun initialState(input: I): S + fun initialState(input: InputT): StateT fun onInputChanged( - old: I, - new: I, - state: S - ): S = state + old: InputT, + new: InputT, + state: StateT + ): StateT = state - fun compose( - input: I, - state: S, - context: WorkflowContext - ): R + fun render( + input: InputT, + state: StateT, + context: RenderContext + ): RenderingT - fun snapshotState(state: S): Snapshot - fun restoreState(snapshot: Snapshot): S + fun snapshotState(state: StateT): Snapshot + fun restoreState(snapshot: Snapshot): StateT } @@ -91,35 +91,35 @@ When the workflow is first started, it is queried for an initial state value. Fr ### Workflows produce an external representation of their state via `Rendering` -Immediately after starting up, or after a state transition occurs, a workflow will have its `compose(state:context:)` method called. This method is responsible for creating and returning a value of type `Rendering`. You can think of `Rendering` as the "external state" of the workflow. While a workflow's internal state may contain more detailed or comprehensive state, the `Rendering` (external state) is a type that is useful outside of the workflow. +Immediately after starting up, or after a state transition occurs, a workflow will have its `render(state:context:)` method called. This method is responsible for creating and returning a value of type `Rendering`. You can think of `Rendering` as the "external state" of the workflow. While a workflow's internal state may contain more detailed or comprehensive state, the `Rendering` (external state) is a type that is useful outside of the workflow. When building an interactive application, the `Rendering` type is commonly (but not always) a view model that will drive the UI layer. ### Workflows form a hierarchy (they may have children) -As they produce a `Rendering` value, it is common for workflows to delegate some portion of that work to a _child workflow_. This is also done via the `WorkflowContext` that is passed into the `compose` method. In order to delegate to a child, the parent workflow instantiates the child within the `compose` method. The parent then calls `compose` on the context, with the child workflow as the single argument. The infrastructure will spin up the child workflow (including initializing its initial state) if this is the first time this child has been used, or, if the child was also used on the previous `compose` pass, the existing child will be updated. Either way, `compose` will ultimately be called on the child (by the Workflow infrastructure), and the resulting `Child.Rendering` value will be returned to the parent. +As they produce a `Rendering` value, it is common for workflows to delegate some portion of that work to a _child workflow_. This is also done via the `RenderContext` that is passed into the `render` method. In order to delegate to a child, the parent workflow instantiates the child within the `render` method. The parent then calls `render` on the context, with the child workflow as the single argument. The infrastructure will spin up the child workflow (including initializing its initial state) if this is the first time this child has been used, or, if the child was also used on the previous `render` pass, the existing child will be updated. Either way, `render` will ultimately be called on the child (by the Workflow infrastructure), and the resulting `Child.Rendering` value will be returned to the parent. This allows a parent to return complex `Rendering` types (such as a view model representing the entire UI state of an application) without needing to model all of that complexity within a single workflow. ### Workflows can respond to UI events -The `WorkflowContext` that is passed into `compose` as the second parameter provides some useful tools to assist in creating the `Rendering` value. +The `RenderContext` that is passed into `render` as the second parameter provides some useful tools to assist in creating the `Rendering` value. -If a workflow is producing a view model, it is common to need an event handler to respond to UI events. The `WorkflowContext` has API to create an event handler that, when called, will advance the workflow by dispatching an action back to the workflow. +If a workflow is producing a view model, it is common to need an event handler to respond to UI events. The `RenderContext` has API to create an event handler that, when called, will advance the workflow by dispatching an action back to the workflow. ### Workflows can subscribe to external event sources -If a workflow needs to respond to some external event source (e.g. push notifications), the workflow can ask the context to listen to those events from within the `compose` method. +If a workflow needs to respond to some external event source (e.g. push notifications), the workflow can ask the context to listen to those events from within the `render` method. ### Workflows can perform asynchronous tasks (Workers) `Workers` are very similar in concept to child workflows. Unlike child workflows, however, workers do not have a `Rendering` type; they only exist to perform a single asynchronous task before sending an output event back up the tree to their parent. -A workflow can ask the infrastructure to await the result of a worker by handing that worker to the context within a call to the `compose` method. +A workflow can ask the infrastructure to await the result of a worker by handing that worker to the context within a call to the `render` method. ### Workflows are advanced by `Action`s diff --git a/docs/faq.md b/docs/faq.md index 8abc2c5e9..3995d9d56 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -9,7 +9,7 @@ |---|---|---|---| | **Modularity** | `Component` | TK | `Workflow` is analogous to React's `Component` | | **State** | Each `Component` has a `state` property that is read directly and updated via a `setState` method. | State is called `Model` in Elm. | `Workflow`s have an associated state type. The state can only be updated when the input changes, or with a `WorkflowAction`. | -| **Views** | `Component`s have a `render` method that returns a tree of elements. | Elm applications have a `view` function that returns a tree of elements. | Since workflows are not tied to any particular UI view layer, they can have an arbitrary rendering type. The `compose()` method returns this type. | +| **Views** | `Component`s have a `render` method that returns a tree of elements. | Elm applications have a `view` function that returns a tree of elements. | Since workflows are not tied to any particular UI view layer, they can have an arbitrary rendering type. The `render()` method returns this type. | | **Dependencies** | React allows parent components to pass "props" down to their children. | TK | In Swift, `Workflow`s are often structs that need to be initialized with their dependencies and configuration data from their parent. In Kotlin, they have a separate type parameter (`InputT`) that is always passed down from the parent. `Workflow` instances can also inject dependencies, and play nicely with dependency injection frameworks. | **Composability** | TK | TK | TK | | **Event Handling** | TK | TK | TK | diff --git a/docs/swift/building-a-workflow.md b/docs/swift/building-a-workflow.md index 69908d33d..2d21e7b6a 100644 --- a/docs/swift/building-a-workflow.md +++ b/docs/swift/building-a-workflow.md @@ -28,7 +28,7 @@ extension DemoWorkflow { } - func compose(state: State, context: WorkflowContext) -> String { + func render(state: State, context: RenderContext) -> String { return "Hello, \(name)" } @@ -39,23 +39,23 @@ A type conforming to `Workflow` represents a single node in the workflow tree. I Configuration parameters, strings, network services… If your workflow needs access to a value or object that it cannot create itself, they should be passed into the workflow's initializer. -Every workflow defines its own `State` type to contain any data that should persist through subsequent compose passes. +Every workflow defines its own `State` type to contain any data that should persist through subsequent render passes. -## Compose +## Render -Workflows are only useful when they render a value for use by their parent (or, if they are the root workflow, for display). This type is very commonly a view model, or `Screen`. The `compose(state:context:)` method has a couple of parameters, so we’ll work through them one by one. +Workflows are only useful when they render a value for use by their parent (or, if they are the root workflow, for display). This type is very commonly a view model, or `Screen`. The `render(state:context:)` method has a couple of parameters, so we’ll work through them one by one. ```swift -func compose(state: State, context: WorkflowContext) -> Rendering +func render(state: State, context: RenderContext) -> Rendering ``` ### `state` -Contains a value of type `State` to provide access to the current state. Any time the state of workflow changes, `compose` is called again to take into account the change in state. +Contains a value of type `State` to provide access to the current state. Any time the state of workflow changes, `render` is called again to take into account the change in state. ### `context` -The workflow context: +The render context: - provides a way for a workflow to defer to nested (child) workflows to generate some or all of its rendered output. We’ll walk through that process later on when we cover composition. - allows a workflow to request the execution of asynchronous tasks (`Worker`s) - generates event handlers for use in constructing view models. @@ -63,7 +63,7 @@ The workflow context: In order for us to see the anything in our app, we'll need to return a `Screen` that can be turned into a view controller: ```swift - func compose(state: State, context: WorkflowContext) -> DemoScreen { + func render(state: State, context: RenderContext) -> DemoScreen { return DemoScreen(title: "A nice title") } ``` @@ -102,12 +102,12 @@ There are two things that the `apply(toState:)` method is responsible for: - Transitioning state - (Optionally) emitting an output event -Note that the `compose(state:context:)` method is called after every state change, so you can be sure that any state changes will be reflected. +Note that the `render(state:context:)` method is called after every state change, so you can be sure that any state changes will be reflected. Since we have a way of expressing an event from our UI, we can now use the callback on our view model to send that event back to the workflow: ```swift -func compose(state: State, context: WorkflowContext) -> DemoScreen { +func render(state: State, context: RenderContext) -> DemoScreen { // Create a sink of our Action type so we can send actions back to the workflow. let sink = context.makeSink(of: Action.self) @@ -170,10 +170,10 @@ struct AsyncWorker: Worker { Because a Worker is a declarative representation of work, it also needs to define an `isEquivalent` to guarantee that we are not running more than one at the same time. For the simple example above, it is always considered equivalent as we want only one of this type of worker running at a time. -In order to start asynchronous work, the workflow requests it in the compose method, looking something like: +In order to start asynchronous work, the workflow requests it in the render method, looking something like: ```swift - public func compose(state: State, context: WorkflowContext) -> DemoScreen { + public func render(state: State, context: RenderContext) -> DemoScreen { context.awaitResult(for: AsyncWorker()) { output -> Action in switch output { @@ -204,17 +204,17 @@ Workflows can define an output type, which may then be returned by Actions. Composition is the primary tool that we can use to manage complexity in a growing application. Workflows should always be kept small enough to be understandable – less than 150 lines is a good target. By composing together multiple workflows, complex problems can be broken down into individual pieces that can be quickly understood by other developers (including future you). -The context provided to the `compose(state:context:)` method defines the API through which composition is made possible. +The context provided to the `render(state:context:)` method defines the API through which composition is made possible. -### The Workflow Context +### The Render Context -The useful role of children is ultimately to provide rendered values (typically screen models) via their `compose(state:context:)` implementation. To obtain that value from a child workflow, the `rendered(with context:key:)` method is invoked on the child workflow. +The useful role of children is ultimately to provide rendered values (typically screen models) via their `render(state:context:)` implementation. To obtain that value from a child workflow, the `rendered(with context:key:)` method is invoked on the child workflow. When a workflow is rendered with the context, the context will do the following: - Check if the child workflow is new or existing: - If a workflow with the same type was used during the last render pass, the existing child workflow will be updated with the new workflow. - Otherwise, a new child workflow node will be initialized. -- The child workflow's `compose(state:context:)` method is called. +- The child workflow's `render(state:context:)` method is called. - The rendered value is returned. In practice, this looks something like this: @@ -222,7 +222,7 @@ In practice, this looks something like this: ```swift struct ParentWorkflow: Workflow { - func compose(state: State, context: WorkflowContext) -> String { + func render(state: State, context: RenderContext) -> String { let childWorkflow = ChildWorkflow(text: "Hello, World") return childWorkflow.rendered(with: context) } @@ -235,7 +235,7 @@ struct ChildWorkflow: Workflow { // ... - func compose(state: State, context: WorkflowContext) -> String { + func render(state: State, context: RenderContext) -> String { return String(text.reversed()) } } diff --git a/docs/swift/using-a-workflow-for-ui.md b/docs/swift/using-a-workflow-for-ui.md index 7e579c7be..8455b16da 100644 --- a/docs/swift/using-a-workflow-for-ui.md +++ b/docs/swift/using-a-workflow-for-ui.md @@ -63,4 +63,4 @@ let container = ContainerViewController( viewRegistry: viewRegistry) ``` -Now, when the `ContainerViewController` is shown, it will start the workflow and `compose` will be called returning the `DemoScreen`. The container will use the view registry to map the `DemoScreen` to a `DemoScreenViewController` and add it to the view hierarchy to display. +Now, when the `ContainerViewController` is shown, it will start the workflow and `render` will be called returning the `DemoScreen`. The container will use the view registry to map the `DemoScreen` to a `DemoScreenViewController` and add it to the view hierarchy to display. diff --git a/swift/Tooling/Templates/Workflow (Verbose).xctemplate/___FILEBASENAME___Workflow.swift b/swift/Tooling/Templates/Workflow (Verbose).xctemplate/___FILEBASENAME___Workflow.swift index 68785e81a..935969cc2 100644 --- a/swift/Tooling/Templates/Workflow (Verbose).xctemplate/___FILEBASENAME___Workflow.swift +++ b/swift/Tooling/Templates/Workflow (Verbose).xctemplate/___FILEBASENAME___Workflow.swift @@ -79,7 +79,7 @@ extension ___VARIABLE_productName___Workflow { extension ___VARIABLE_productName___Workflow { - func compose(state: ___VARIABLE_productName___Workflow.State, context: WorkflowContext<___VARIABLE_productName___Workflow>) -> String { + func render(state: ___VARIABLE_productName___Workflow.State, context: RenderContext<___VARIABLE_productName___Workflow>) -> String { #warning("Don't forget your compose implementation and to return the correct rendering type!") return "This is likely not the rendering that you want to return" } diff --git a/swift/Workflow/Sources/AnyWorkflow.swift b/swift/Workflow/Sources/AnyWorkflow.swift index 0c0a3db03..84f5433bd 100644 --- a/swift/Workflow/Sources/AnyWorkflow.swift +++ b/swift/Workflow/Sources/AnyWorkflow.swift @@ -63,7 +63,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(context: WorkflowContext, key: String, outputMap: @escaping (Output) -> AnyWorkflowAction) -> Rendering { + internal func render(context: RenderContext, key: String, outputMap: @escaping (Output) -> AnyWorkflowAction) -> Rendering { return storage.render(context: context, key: key, outputMap: outputMap) } @@ -76,7 +76,7 @@ extension AnyWorkflow { /// This type is never used directly. fileprivate class AnyStorage { - func render(context: WorkflowContext, key: String, outputMap: @escaping (Output) -> AnyWorkflowAction) -> Rendering { + func render(context: RenderContext, key: String, outputMap: @escaping (Output) -> AnyWorkflowAction) -> Rendering { fatalError() } @@ -113,7 +113,7 @@ extension AnyWorkflow { return T.self } - override func render(context: WorkflowContext, key: String, outputMap: @escaping (Output) -> AnyWorkflowAction) -> Rendering { + override func render(context: RenderContext, key: String, outputMap: @escaping (Output) -> AnyWorkflowAction) -> Rendering { let outputMap: (T.Output) -> AnyWorkflowAction = { [outputTransform] output in return outputMap(outputTransform(output)) } diff --git a/swift/Workflow/Sources/AnyWorkflowConvertible.swift b/swift/Workflow/Sources/AnyWorkflowConvertible.swift index d788c7a3a..2f15c104c 100644 --- a/swift/Workflow/Sources/AnyWorkflowConvertible.swift +++ b/swift/Workflow/Sources/AnyWorkflowConvertible.swift @@ -25,11 +25,11 @@ extension AnyWorkflowConvertible { /// - Parameter key: A string that uniquely identifies this workflow. /// /// - Returns: The `Rendering` generated by the workflow. - public func rendered(with context: WorkflowContext, key: String = "") -> Rendering where Output: WorkflowAction, Output.WorkflowType == Parent { + public func rendered(with context: RenderContext, key: String = "") -> Rendering where Output: WorkflowAction, Output.WorkflowType == Parent { return asAnyWorkflow().render(context: context, key: key, outputMap: { AnyWorkflowAction($0) }) } - public func rendered(with context: WorkflowContext, key: String = "") -> Rendering where Output == AnyWorkflowAction { + public func rendered(with context: RenderContext, key: String = "") -> Rendering where Output == AnyWorkflowAction { return asAnyWorkflow().render(context: context, key: key, outputMap: { $0 }) } @@ -46,7 +46,7 @@ extension AnyWorkflowConvertible where Output == Never { /// - Parameter key: A string that uniquely identifies this workflow. /// /// - Returns: The `Rendering` generated by the workflow. - public func rendered(with context: WorkflowContext, key: String = "") -> Rendering { + public func rendered(with context: RenderContext, key: String = "") -> Rendering { // Convenience for workflow that have no output allowing them to be rendered with any context return asAnyWorkflow() diff --git a/swift/Workflow/Sources/WorkflowContext.swift b/swift/Workflow/Sources/RenderContext.swift similarity index 83% rename from swift/Workflow/Sources/WorkflowContext.swift rename to swift/Workflow/Sources/RenderContext.swift index ed460e529..cb9eb0c86 100644 --- a/swift/Workflow/Sources/WorkflowContext.swift +++ b/swift/Workflow/Sources/RenderContext.swift @@ -1,9 +1,9 @@ import ReactiveSwift import Result -/// `WorkflowContext` is the composition point for the workflow tree. +/// `RenderContext` is the composition point for the workflow tree. /// -/// During a compose pass, a workflow may want to defer to a child +/// During a render pass, a workflow may want to defer to a child /// workflow to render some portion of its content. For example, /// a workflow that renders to a split-screen view model might /// delegate to child A for the left side, and child B for the right @@ -29,9 +29,9 @@ import Result /// If the parent had not rendered a child of the same type in the previous /// render pass, a new child workflow node is generated. /// -/// The infrastructure then performs a compose pass on the child to obtain its +/// The infrastructure then performs a render pass on the child to obtain its /// `Rendering` value, which is then returned to the caller. -public class WorkflowContext: WorkflowContextType { +public class RenderContext: RenderContextType { private (set) var isValid = true @@ -48,7 +48,7 @@ public class WorkflowContext: WorkflowContextType { /// - Parameter outputMap: A closure that transforms the child's output type into `Action`. /// - Parameter key: A string that uniquely identifies this child. /// - /// - Returns: The `Rendering` result of the child's `compose` method. + /// - Returns: The `Rendering` result of the child's `render` method. public func render(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Child : Workflow, Action : WorkflowAction, WorkflowType == Action.WorkflowType { fatalError() } @@ -65,14 +65,14 @@ public class WorkflowContext: WorkflowContextType { isValid = false } - // API to allow custom context implementations to power a workflow context - static func make(implementation: T) -> WorkflowContext where T.WorkflowType == WorkflowType { - return ConcreteWorkflowContext(implementation) + // API to allow custom context implementations to power a render context + static func make(implementation: T) -> RenderContext where T.WorkflowType == WorkflowType { + return ConcreteRenderContext(implementation) } - // Private subclass that forwards render calls to a wrapped implementation. This is the only `WorkflowContext` class + // Private subclass that forwards render calls to a wrapped implementation. This is the only `RenderContext` class // that is ever instantiated. - private final class ConcreteWorkflowContext: WorkflowContext where WorkflowType == T.WorkflowType { + private final class ConcreteRenderContext: RenderContext where WorkflowType == T.WorkflowType { let implementation: T @@ -98,7 +98,7 @@ public class WorkflowContext: WorkflowContextType { } private func assertStillValid() { - assert(isValid, "A `WorkflowContext` instance was used outside of the workflow's `render` method. It is a programmer error to capture a context in a closure or otherwise cause it to be used outside of the `render` method.") + assert(isValid, "A `RenderContext` instance was used outside of the workflow's `render` method. It is a programmer error to capture a context in a closure or otherwise cause it to be used outside of the `render` method.") } } @@ -106,7 +106,7 @@ public class WorkflowContext: WorkflowContextType { } -internal protocol WorkflowContextType: class { +internal protocol RenderContextType: class { associatedtype WorkflowType: Workflow func render(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Child: Workflow, Action: WorkflowAction, Action.WorkflowType == WorkflowType @@ -118,7 +118,7 @@ internal protocol WorkflowContextType: class { } -extension WorkflowContext { +extension RenderContext { public func makeSink(of actionType: Action.Type) -> Sink where Action: WorkflowAction, Action.WorkflowType == WorkflowType { let (signal, observer) = Signal, NoError>.pipe() @@ -140,7 +140,7 @@ extension WorkflowContext { } -extension WorkflowContext { +extension RenderContext { public func awaitResult(for worker: W) where W : Worker, W.Output : WorkflowAction, WorkflowType == W.Output.WorkflowType { awaitResult(for: worker, outputMap: { $0 }) diff --git a/swift/Workflow/Sources/SubtreeManager.swift b/swift/Workflow/Sources/SubtreeManager.swift index ccc25a6da..996425d53 100644 --- a/swift/Workflow/Sources/SubtreeManager.swift +++ b/swift/Workflow/Sources/SubtreeManager.swift @@ -30,14 +30,14 @@ extension WorkflowNode { } /// Performs an update pass using the given closure. - func render(_ actions: (WorkflowContext) -> Rendering) -> Rendering { + func render(_ actions: (RenderContext) -> Rendering) -> Rendering { /// Create a workflow context containing the existing children let context = Context( originalChildWorkflows: childWorkflows, originalChildWorkers: childWorkers) - let wrapped = WorkflowContext.make(implementation: context) + let wrapped = RenderContext.make(implementation: context) /// Pass the context into the closure to allow a render to take place let rendering = actions(wrapped) @@ -108,7 +108,7 @@ extension WorkflowNode.SubtreeManager { extension WorkflowNode.SubtreeManager { /// The workflow context implementation used by the subtree manager. - fileprivate final class Context: WorkflowContextType { + fileprivate final class Context: RenderContextType { private let originalChildWorkflows: [ChildKey:AnyChildWorkflow] private (set) internal var usedChildWorkflows: [ChildKey:AnyChildWorkflow] diff --git a/swift/Workflow/Sources/Worker.swift b/swift/Workflow/Sources/Worker.swift index 16bf5bdd7..49182ff1e 100644 --- a/swift/Workflow/Sources/Worker.swift +++ b/swift/Workflow/Sources/Worker.swift @@ -3,7 +3,7 @@ import Result /// Workers define a unit of asynchronous work. /// -/// During a compose pass, a workflow can ask the context to await the result of a worker. +/// During a render pass, a workflow can ask the context to await the result of a worker. /// /// When this occurs, the context checks to see if there is already a running worker of the same type. /// If there is, and if the workers are 'equivalent', the context leaves the existing worker running. diff --git a/swift/Workflow/Sources/Workflow.swift b/swift/Workflow/Sources/Workflow.swift index 1f0d18824..ad74feefe 100644 --- a/swift/Workflow/Sources/Workflow.swift +++ b/swift/Workflow/Sources/Workflow.swift @@ -8,8 +8,8 @@ import Result /// *** /// /// A workflow node comes into existence after its parent produces -/// an instance of that workflow and uses it during a compose pass (see the -/// `compose` method for more details). +/// an instance of that workflow and uses it during a render pass (see the +/// `render` method for more details). /// /// - If this is the first time the parent has rendered a child of /// this type, a new workflow node is created. The workflow @@ -22,16 +22,16 @@ import Result /// to allow the workflow to respond to the change. /// /// *** -/// **Compose** +/// **Render** /// *** /// After a workflow node has been created, or any time its state changes, -/// a compose pass occurs. The compose pass takes the workflow that was passed +/// a render pass occurs. The render pass takes the workflow that was passed /// down from the parent along with the current state and generates a value /// of type `Rendering`. In a common case, a workflpw might render to a screen /// model for display. /// /// ``` -/// func compose(state: State, context: WorkflowContext) -> MyScreenModel { +/// func render(state: State, context: RenderContext) -> MyScreenModel { /// return MyScreenModel() /// } /// ``` @@ -44,7 +44,7 @@ public protocol Workflow: AnyWorkflowConvertible { /// `Output` defines the type that can be emitted as output events. associatedtype Output = Never - /// `Rendering` is the type that is produced by the `compose` method: it + /// `Rendering` is the type that is produced by the `render` method: it /// is commonly a view / screen model. associatedtype Rendering @@ -59,7 +59,7 @@ public protocol Workflow: AnyWorkflowConvertible { /// - Parameter state: The current state. func workflowDidChange(from previousWorkflow: Self, state: inout State) - /// Called to "compose" the current state into `Rendering`. A workflow's `Rendering` type is commonly a view or + /// Called to "render" the current state into `Rendering`. A workflow's `Rendering` type is commonly a view or /// screen model. /// /// - Parameter state: The current state. @@ -67,7 +67,7 @@ public protocol Workflow: AnyWorkflowConvertible { /// workflow, instantiate it based on the current state. The newly instantiated workflow is /// then used to invoke `context.render(_ workflow:)`, which returns the child's `Rendering` /// type after creating or updating the nested workflow. - func compose(state: State, context: WorkflowContext) -> Rendering + func render(state: State, context: RenderContext) -> Rendering } diff --git a/swift/Workflow/Sources/WorkflowNode.swift b/swift/Workflow/Sources/WorkflowNode.swift index f41be9bb3..a5d073d0d 100644 --- a/swift/Workflow/Sources/WorkflowNode.swift +++ b/swift/Workflow/Sources/WorkflowNode.swift @@ -58,7 +58,7 @@ final class WorkflowNode { func render() -> WorkflowType.Rendering { return subtreeManager.render { context in return workflow - .compose( + .render( state: state, context: context) } diff --git a/swift/Workflow/Tests/AnyWorkflowTests.swift b/swift/Workflow/Tests/AnyWorkflowTests.swift index 5b7c2c580..8e3f55de4 100644 --- a/swift/Workflow/Tests/AnyWorkflowTests.swift +++ b/swift/Workflow/Tests/AnyWorkflowTests.swift @@ -44,7 +44,7 @@ extension PassthroughWorkflow { } - func compose(state: State, context: WorkflowContext>) -> Rendering { + func render(state: State, context: RenderContext>) -> Rendering { return child.rendered(with: context) } @@ -70,7 +70,7 @@ extension SimpleWorkflow { } - func compose(state: State, context: WorkflowContext) -> String { + func render(state: State, context: RenderContext) -> String { return String(string.reversed()) } diff --git a/swift/Workflow/Tests/SubtreeManagerTests.swift b/swift/Workflow/Tests/SubtreeManagerTests.swift index e542f3c41..d8c306e2c 100644 --- a/swift/Workflow/Tests/SubtreeManagerTests.swift +++ b/swift/Workflow/Tests/SubtreeManagerTests.swift @@ -106,7 +106,7 @@ final class SubtreeManagerTests: XCTestCase { func test_invalidatesContextAfterRender() { let manager = WorkflowNode.SubtreeManager() - var escapingContext: WorkflowContext! = nil + var escapingContext: RenderContext! = nil _ = manager.render { context -> TestViewModel in XCTAssertTrue(context.isValid) @@ -141,7 +141,7 @@ fileprivate struct ParentWorkflow: Workflow { } - func compose(state: State, context: WorkflowContext) -> Never { + func render(state: State, context: RenderContext) -> Never { fatalError() } } @@ -186,7 +186,7 @@ fileprivate struct TestWorkflow: Workflow { } - func compose(state: State, context: WorkflowContext) -> TestViewModel { + func render(state: State, context: RenderContext) -> TestViewModel { let sink = context.makeSink(of: Event.self) diff --git a/swift/Workflow/Tests/WorkflowNodeTests.swift b/swift/Workflow/Tests/WorkflowNodeTests.swift index b6e315cca..d058adec1 100644 --- a/swift/Workflow/Tests/WorkflowNodeTests.swift +++ b/swift/Workflow/Tests/WorkflowNodeTests.swift @@ -190,7 +190,7 @@ final class WorkflowNodeTests: XCTestCase { } - func compose(state: WF.State, context: WorkflowContext) -> Void { + func render(state: WF.State, context: RenderContext) -> Void { context.awaitResult(for: TestWorker()) { output in return AnyWorkflowAction(sendingOutput: output) } @@ -285,7 +285,7 @@ extension CompositeWorkflow { - func compose(state: State, context: WorkflowContext>) -> Rendering { + func render(state: State, context: RenderContext>) -> Rendering { return Rendering( @@ -333,7 +333,7 @@ fileprivate struct SimpleWorkflow: Workflow { } - func compose(state: State, context: WorkflowContext) -> String { + func render(state: State, context: RenderContext) -> String { return String(string.reversed()) } @@ -380,7 +380,7 @@ extension EventEmittingWorkflow { } - func compose(state: State, context: WorkflowContext) -> Rendering { + func render(state: State, context: RenderContext) -> Rendering { let sink = context.makeSink(of: Event.self) @@ -410,7 +410,7 @@ fileprivate struct StateTransitioningWorkflow: Workflow { } - func compose(state: State, context: WorkflowContext) -> Rendering { + func render(state: State, context: RenderContext) -> Rendering { let sink = context.makeSink(of: Event.self) diff --git a/swift/WorkflowUI/Tests/ContainerViewControllerTests.swift b/swift/WorkflowUI/Tests/ContainerViewControllerTests.swift index e9035d2ed..7de3851b6 100644 --- a/swift/WorkflowUI/Tests/ContainerViewControllerTests.swift +++ b/swift/WorkflowUI/Tests/ContainerViewControllerTests.swift @@ -99,7 +99,7 @@ fileprivate struct MockWorkflow: Workflow { } - func compose(state: State, context: WorkflowContext) -> TestScreen { + func render(state: State, context: RenderContext) -> TestScreen { context.subscribe(signal: subscription.map { output in return AnyWorkflowAction { state in