Skip to content

Commit

Permalink
Merge pull request #211 from wwt/Issue-96
Browse files Browse the repository at this point in the history
Add the ability to abandon workflow when a publisher emits a value
  • Loading branch information
Tyler-Keith-Thompson committed Aug 14, 2022
2 parents 16c0142 + aefb179 commit 16b0dac
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 4 deletions.
24 changes: 20 additions & 4 deletions Sources/SwiftCurrent_SwiftUI/Views/WorkflowLauncher.swift
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2021 WWT and Tyler Thompson. All rights reserved.
//

import Combine
import SwiftUI
import SwiftCurrent

Expand All @@ -15,6 +16,7 @@ public struct WorkflowLauncher<Content: _WorkflowItemProtocol>: View {
public typealias WorkflowInput = Content.FlowRepresentableType.WorkflowInput

@WorkflowBuilder private var content: Content
@State private var abandonOnPublisher: AnyPublisher<Void, Never> = Empty(completeImmediately: false).eraseToAnyPublisher()
@State private var onFinish = [(AnyWorkflow.PassedArgs) -> Void]()
@State private var onAbandon = [() -> Void]()
@State private var shouldEmbedInNavView = false
Expand All @@ -38,6 +40,7 @@ public struct WorkflowLauncher<Content: _WorkflowItemProtocol>: View {
}
}
.onChange(of: isLaunched) { if $0 == false { resetWorkflow() } }
.onReceive(abandonOnPublisher) { launcher.workflow.abandon() }
}

private var workflowContent: some View {
Expand All @@ -61,14 +64,19 @@ public struct WorkflowLauncher<Content: _WorkflowItemProtocol>: View {
self.content = content()
}

private init(current: Self, shouldEmbedInNavView: Bool, onFinish: [(AnyWorkflow.PassedArgs) -> Void], onAbandon: [() -> Void]) {
private init(current: Self,
abandonOnPublisher: AnyPublisher<Void, Never> = Empty(completeImmediately: false).eraseToAnyPublisher(),
shouldEmbedInNavView: Bool,
onFinish: [(AnyWorkflow.PassedArgs) -> Void],
onAbandon: [() -> Void]) {
_model = current._model
_launcher = current._launcher
content = current.content
_isLaunched = current._isLaunched
_shouldEmbedInNavView = State(initialValue: shouldEmbedInNavView)
_onFinish = State(initialValue: onFinish)
_onAbandon = State(initialValue: onAbandon)
_shouldEmbedInNavView = State(wrappedValue: shouldEmbedInNavView)
_onFinish = State(wrappedValue: onFinish)
_onAbandon = State(wrappedValue: onAbandon)
_abandonOnPublisher = State(wrappedValue: abandonOnPublisher)
}

private func resetWorkflow() {
Expand All @@ -92,6 +100,14 @@ public struct WorkflowLauncher<Content: _WorkflowItemProtocol>: View {
return Self(current: self, shouldEmbedInNavView: shouldEmbedInNavView, onFinish: onFinish, onAbandon: onAbandon)
}

func abandonOn<P: Publisher>(_ publisher: P) -> Self where P.Failure == Never {
Self(current: self,
abandonOnPublisher: publisher.map { _ in () }.eraseToAnyPublisher(),
shouldEmbedInNavView: shouldEmbedInNavView,
onFinish: onFinish,
onAbandon: onAbandon)
}

func embedInNavigationView() -> Self {
Self(current: self, shouldEmbedInNavView: true, onFinish: onFinish, onAbandon: onAbandon)
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/SwiftCurrent_SwiftUI/Views/WorkflowView.swift
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2022 WWT and Tyler Thompson. All rights reserved.
//

import Combine
import SwiftUI
import SwiftCurrent

Expand Down Expand Up @@ -202,6 +203,11 @@ public struct WorkflowView<Content: View>: View {
Self(self, newContent: _content.wrappedValue.onAbandon(closure: closure))
}

/// Subscribers to a combine publisher, when a value is emitted the workflow will abandon.
public func abandonOn<WI: _WorkflowItemProtocol, P: Publisher>(_ publisher: P) -> Self where Content == WorkflowLauncher<WI>, P.Failure == Never {
Self(self, newContent: _content.wrappedValue.abandonOn(publisher))
}

/// Wraps content in a NavigationView.
public func embedInNavigationView<WI: _WorkflowItemProtocol>() -> Self where Content == WorkflowLauncher<WI> {
Self(self, newContent: _content.wrappedValue.embedInNavigationView())
Expand Down
27 changes: 27 additions & 0 deletions Tests/SwiftCurrent_SwiftUITests/SwiftCurrent_SwiftUITests.swift
Expand Up @@ -8,6 +8,7 @@

import XCTest
import SwiftUI
import Combine
import ViewInspector

import SwiftCurrent
Expand Down Expand Up @@ -529,6 +530,32 @@ final class SwiftCurrent_SwiftUIConsumerTests: XCTestCase {
wait(for: [expectOnAbandon1, expectOnAbandon2], timeout: TestConstant.timeout)
}

func testWorkflowCanAbandonFromAPublisher() async throws {
let publisher = PassthroughSubject<Void, Never>()
struct FR1: View, FlowRepresentable, Inspectable {
var _workflowPointer: AnyFlowRepresentable?
var body: some View { Text("FR1 type") }
}
let isLaunched = Binding(wrappedValue: true)
let expectOnAbandon = expectation(description: "OnAbandon called")
let launcher = try await MainActor.run {
WorkflowView(isLaunched: isLaunched) {
WorkflowItem(FR1.self)
}
.onAbandon {
XCTAssertFalse(isLaunched.wrappedValue)
expectOnAbandon.fulfill()
}
.abandonOn(publisher)
}.hostAndInspect(with: \.inspection)

XCTAssertEqual(try launcher.find(FR1.self).text().string(), "FR1 type")
publisher.send()
XCTAssertThrowsError(try launcher.find(FR1.self))

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

func testWorkflowCanHaveModifiers() async throws {
struct FR1: View, FlowRepresentable, Inspectable {
var _workflowPointer: AnyFlowRepresentable?
Expand Down

0 comments on commit 16b0dac

Please sign in to comment.