Skip to content

Commit

Permalink
Merge pull request #91 from wwt/state-craziness
Browse files Browse the repository at this point in the history
Fixes issue with changing parent view state and vanishing Workflow Views
  • Loading branch information
brianlombardo authored Aug 3, 2021
2 parents 82cf622 + 60ceddd commit 562cd51
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 20 deletions.
41 changes: 21 additions & 20 deletions Sources/SwiftCurrent_SwiftUI/Views/ModifiedWorkflowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ public struct ModifiedWorkflowView<Args, Wrapped: View, Content: View>: View {
@Binding private var isLaunched: Bool

let inspection = Inspection<Self>()
private let wrapped: Wrapped?
private let workflow: AnyWorkflow
private let launchArgs: AnyWorkflow.PassedArgs
private var onFinish = [(AnyWorkflow.PassedArgs) -> Void]()
private var onAbandon = [() -> Void]()
// These need to be state variables to survive SwiftUI re-rendering. Change under penalty of torture BY the codebase you modified.
@State private var wrapped: Wrapped?
@State private var workflow: AnyWorkflow
@State private var launchArgs: AnyWorkflow.PassedArgs
@State private var onFinish = [(AnyWorkflow.PassedArgs) -> Void]()
@State private var onAbandon = [() -> Void]()

@StateObject private var model: WorkflowViewModel
@StateObject private var launcher: Launcher
Expand All @@ -47,13 +48,13 @@ public struct ModifiedWorkflowView<Args, Wrapped: View, Content: View>: View {
}

init<A, FR>(_ workflowLauncher: WorkflowLauncher<A>, isLaunched: Binding<Bool>, item: WorkflowItem<FR, Content>) where Wrapped == Never, Args == FR.WorkflowOutput {
wrapped = nil
_wrapped = State(initialValue: nil)
let wf = AnyWorkflow(Workflow<FR>(item.metadata))
workflow = wf
launchArgs = workflowLauncher.passedArgs
_workflow = State(initialValue: wf)
_launchArgs = State(initialValue: workflowLauncher.passedArgs)
_isLaunched = isLaunched
onFinish = workflowLauncher.onFinish
onAbandon = workflowLauncher.onAbandon
_onFinish = State(initialValue: workflowLauncher.onFinish)
_onAbandon = State(initialValue: workflowLauncher.onAbandon)
let model = WorkflowViewModel(isLaunched: isLaunched, launchArgs: workflowLauncher.passedArgs)
_model = StateObject(wrappedValue: model)
_launcher = StateObject(wrappedValue: Launcher(workflow: wf,
Expand All @@ -63,22 +64,22 @@ public struct ModifiedWorkflowView<Args, Wrapped: View, Content: View>: View {

private init<A, W, C, FR>(_ workflowLauncher: ModifiedWorkflowView<A, W, C>, item: WorkflowItem<FR, Content>) where Wrapped == ModifiedWorkflowView<A, W, C>, Args == FR.WorkflowOutput {
_model = workflowLauncher._model
wrapped = workflowLauncher
workflow = workflowLauncher.workflow
workflow.append(item.metadata)
launchArgs = workflowLauncher.launchArgs
_wrapped = State(initialValue: workflowLauncher)
_workflow = workflowLauncher._workflow
_launchArgs = workflowLauncher._launchArgs
_isLaunched = workflowLauncher._isLaunched
_launcher = workflowLauncher._launcher
onAbandon = workflowLauncher.onAbandon
_onAbandon = workflowLauncher._onAbandon
workflow.append(item.metadata)
}

private init(workflowLauncher: Self, onFinish: [(AnyWorkflow.PassedArgs) -> Void], onAbandon: [() -> Void]) {
_model = workflowLauncher._model
wrapped = workflowLauncher.wrapped
workflow = workflowLauncher.workflow
self.onFinish = onFinish
self.onAbandon = onAbandon
launchArgs = workflowLauncher.launchArgs
_wrapped = workflowLauncher._wrapped
_workflow = workflowLauncher._workflow
_onFinish = State(initialValue: onFinish)
_onAbandon = State(initialValue: onAbandon)
_launchArgs = workflowLauncher._launchArgs
_isLaunched = workflowLauncher._isLaunched
_launcher = workflowLauncher._launcher
}
Expand Down
44 changes: 44 additions & 0 deletions Tests/SwiftCurrent_SwiftUITests/SwiftCurrent_SwiftUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -627,4 +627,48 @@ final class SwiftCurrent_SwiftUIConsumerTests: XCTestCase {

wait(for: [expectOnFinish, expectViewLoaded], timeout: TestConstant.timeout)
}

func testWorkflowCorrectlyHandlesState() throws {
final class Launcher: ObservableObject {
var onFinishCalled = false
init(workflow: AnyWorkflow,
responder: OrchestrationResponder,
launchArgs: AnyWorkflow.PassedArgs) {
if workflow.orchestrationResponder == nil {
workflow.launch(withOrchestrationResponder: responder, passedArgs: launchArgs)
}
}
}
struct ModifiedWorkflowViewExplorer<A, W, C>: View, Inspectable {
@Binding var isLaunched: Bool

let inspection = Inspection<Self>()
@State var wrapped: W?
@State var workflow: AnyWorkflow
@State var launchArgs: AnyWorkflow.PassedArgs
@State var onFinish = [(AnyWorkflow.PassedArgs) -> Void]()
@State var onAbandon = [() -> Void]()

@StateObject private var model: WorkflowViewModel
@StateObject private var launcher: Launcher

var body: some View { EmptyView() }
}

struct FR1: View, FlowRepresentable {
weak var _workflowPointer: AnyFlowRepresentable?

var body: some View {
Button("Proceed") { proceedInWorkflow() }
}
}

let workflowView = WorkflowLauncher(isLaunched: .constant(true))
.thenProceed(with: WorkflowItem(FR1.self))

let explorer = unsafeBitCast(workflowView, to: ModifiedWorkflowViewExplorer<Never, Never, FR1>.self)

// if the unsafeBitCast worked, then everything is a state var.
XCTAssertNil(explorer.wrapped)
}
}

0 comments on commit 562cd51

Please sign in to comment.