-
Notifications
You must be signed in to change notification settings - Fork 47
RF-9688 - [refactor]: Remove ReactiveSwift from Workflow public interface #360
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ | |
* limitations under the License. | ||
*/ | ||
|
||
import ReactiveSwift | ||
import Combine | ||
|
||
/// Defines a type that receives debug information about a running workflow hierarchy. | ||
public protocol WorkflowDebugger { | ||
|
@@ -30,18 +30,30 @@ public protocol WorkflowDebugger { | |
func didUpdate(snapshot: WorkflowHierarchyDebugSnapshot, updateInfo: WorkflowUpdateDebugInfo) | ||
} | ||
|
||
/// Manages an active workflow hierarchy. | ||
public final class WorkflowHost<WorkflowType: Workflow> { | ||
private let (outputEvent, outputEventObserver) = Signal<WorkflowType.Output, Never>.pipe() | ||
public protocol WorkflowOutputPublisher { | ||
associatedtype Output | ||
|
||
var outputPublisher: AnyPublisher<Output, Never> { get } | ||
} | ||
|
||
/// Manages an active workflow hierarchy. | ||
public final class WorkflowHost<WorkflowType: Workflow>: WorkflowOutputPublisher { | ||
// @testable | ||
let rootNode: WorkflowNode<WorkflowType> | ||
|
||
private let mutableRendering: MutableProperty<WorkflowType.Rendering> | ||
private let renderingSubject: CurrentValueSubject<WorkflowType.Rendering, Never> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. slightly inclined to split the storage for the property vs the observer, since that will give us the most control over when the 'outside world' sees the update. e.g.
any thoughts on that? i guess one thing that would be different is that the old way (and presumably using a CVS) would have some baked-in synchronization mechanism over the underlying value. in theory this stuff should be main-thread-only though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We currently are using a ReactiveSwift MutableProperty which does have an internal lock. Yes CurrentValueSubject does have some baked in serialization mechanism albeit without Apple documentation on what that is. Since we can't enforce someone reading the rendering value on the main thread I'm inclined to use the CurrentValueSubject since it's about as close as we can get as a Combine version of what we have now. |
||
private let outputSubject = PassthroughSubject<WorkflowType.Output, Never>() | ||
|
||
/// Represents the `Rendering` produced by the root workflow in the hierarchy. New `Rendering` values are produced | ||
/// as state transitions occur within the hierarchy. | ||
public let rendering: Property<WorkflowType.Rendering> | ||
public var rendering: WorkflowType.Rendering { | ||
renderingSubject.value | ||
} | ||
|
||
/// A Publisher containing rendering events produced by the root workflow in the hierarchy. | ||
public var renderingPublisher: AnyPublisher<WorkflowType.Rendering, Never> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. any thoughts on using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer |
||
renderingSubject.eraseToAnyPublisher() | ||
} | ||
|
||
/// Context object to pass down to descendant nodes in the tree. | ||
let context: HostContext | ||
|
@@ -78,8 +90,8 @@ public final class WorkflowHost<WorkflowType: Workflow> { | |
parentSession: nil | ||
) | ||
|
||
self.mutableRendering = MutableProperty(rootNode.render()) | ||
self.rendering = Property(mutableRendering) | ||
self.renderingSubject = CurrentValueSubject(rootNode.render()) | ||
|
||
rootNode.enableEvents() | ||
|
||
debugger?.didEnterInitialState(snapshot: rootNode.makeDebugSnapshot()) | ||
|
@@ -110,12 +122,12 @@ public final class WorkflowHost<WorkflowType: Workflow> { | |
private func handle(output: WorkflowNode<WorkflowType>.Output) { | ||
let shouldRender = !shouldSkipRenderForOutput(output) | ||
if shouldRender { | ||
mutableRendering.value = rootNode.render() | ||
renderingSubject.send(rootNode.render()) | ||
} | ||
|
||
// Always emit an output, regardless of whether a render occurs | ||
if let outputEvent = output.outputEvent { | ||
outputEventObserver.send(value: outputEvent) | ||
outputSubject.send(outputEvent) | ||
} | ||
|
||
debugger?.didUpdate( | ||
|
@@ -129,9 +141,9 @@ public final class WorkflowHost<WorkflowType: Workflow> { | |
} | ||
} | ||
|
||
/// A signal containing output events emitted by the root workflow in the hierarchy. | ||
public var output: Signal<WorkflowType.Output, Never> { | ||
outputEvent | ||
/// A publisher containing output events emitted by the root workflow in the hierarchy. | ||
public var outputPublisher: AnyPublisher<WorkflowType.Output, Never> { | ||
outputSubject.eraseToAnyPublisher() | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this protocol necessary? what's the benefit over just extending the concrete type directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It allows us to add the output signal on both
WorkflowHost
andWorkflowHostingController
by just extending the protocol in WorkflowReactiveSwift. This was a suggestion in Andrew's feedback.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah gotcha (sorry i missed the existing convo). so the tradeoff here is adding a 'public' protocol to which we really only want 2 specific things to conform so that we don't need to make
Workflow
orWorkflowUI
depend onReactiveSwift
– is that right? mostly out of curiosity – is it possible to define the protocol withpackage
visibility? that seems like it might be a slightly more accurate definition (assuming it works and integrates successfully into the monorepo).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before I had extensions on
WorkflowHost
andWorkflowHostingController
which required a new module just to put the extension onWorkflowHostingController
since I could not put it inWorkflowReactiveSwift
since it does not know aboutWorkflowUI
. Andrew's idea was use a protocol and drop the extension inWorkflowReactiveSwift
so we don't need to have an additional module and the reactive swift stuff stays inWorkflowReactiveSwift
.I can look into the
package
visibility to see if that is possible.