Skip to content

Commit bd12dc3

Browse files
[fix-204] - Fixes #204 - TT
1 parent b4d4a09 commit bd12dc3

File tree

7 files changed

+93
-39
lines changed

7 files changed

+93
-39
lines changed

Sources/SwiftCurrent_SwiftUI/Views/WorkflowItemWrapper.swift

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public struct WorkflowItemWrapper<WI: _WorkflowItemProtocol, Wrapped: _WorkflowI
4848
.onReceive(model.$body, perform: activateIfNeeded)
4949
.onReceive(model.$body, perform: proceedInWorkflow)
5050
.onReceive(model.onBackUpPublisher, perform: backUpInWorkflow)
51+
.onReceive(model.onAbandonPublisher) { isActive = false }
5152
.onReceive(inspection.notice) { inspection.visit(self, $0) }
5253
}
5354

Sources/SwiftCurrent_SwiftUI/Views/WorkflowLauncher.swift

+12-16
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import SwiftCurrent
1414
public struct WorkflowLauncher<Content: _WorkflowItemProtocol>: View {
1515
public typealias WorkflowInput = Content.FlowRepresentableType.WorkflowInput
1616

17-
@State private var content: Content
17+
@WorkflowBuilder private var content: Content
1818
@State private var onFinish = [(AnyWorkflow.PassedArgs) -> Void]()
1919
@State private var onAbandon = [() -> Void]()
2020
@State private var shouldEmbedInNavView = false
@@ -49,32 +49,28 @@ public struct WorkflowLauncher<Content: _WorkflowItemProtocol>: View {
4949
.onReceive(inspection.notice) { inspection.visit(self, $0) }
5050
}
5151

52-
init(isLaunched: Binding<Bool>, startingArgs: AnyWorkflow.PassedArgs, content: () -> Content) {
53-
self.init(isLaunched: isLaunched, startingArgs: startingArgs, content: content())
52+
init(isLaunched: Binding<Bool>, startingArgs: AnyWorkflow.PassedArgs, @WorkflowBuilder content: () -> Content) {
53+
_isLaunched = isLaunched
54+
let wf = AnyWorkflow.empty
55+
content().modify(workflow: wf)
56+
let model = WorkflowViewModel(isLaunched: isLaunched, launchArgs: startingArgs)
57+
_model = StateObject(wrappedValue: model)
58+
_launcher = StateObject(wrappedValue: Launcher(workflow: wf,
59+
responder: model,
60+
launchArgs: startingArgs))
61+
self.content = content()
5462
}
5563

5664
private init(current: Self, shouldEmbedInNavView: Bool, onFinish: [(AnyWorkflow.PassedArgs) -> Void], onAbandon: [() -> Void]) {
5765
_model = current._model
5866
_launcher = current._launcher
59-
_content = current._content
67+
content = current.content
6068
_isLaunched = current._isLaunched
6169
_shouldEmbedInNavView = State(initialValue: shouldEmbedInNavView)
6270
_onFinish = State(initialValue: onFinish)
6371
_onAbandon = State(initialValue: onAbandon)
6472
}
6573

66-
private init(isLaunched: Binding<Bool>, startingArgs: AnyWorkflow.PassedArgs, content: Content) {
67-
_isLaunched = isLaunched
68-
let wf = AnyWorkflow.empty
69-
content.modify(workflow: wf)
70-
let model = WorkflowViewModel(isLaunched: isLaunched, launchArgs: startingArgs)
71-
_model = StateObject(wrappedValue: model)
72-
_launcher = StateObject(wrappedValue: Launcher(workflow: wf,
73-
responder: model,
74-
launchArgs: startingArgs))
75-
_content = State(wrappedValue: content)
76-
}
77-
7874
private func resetWorkflow() {
7975
launcher.workflow.launch(withOrchestrationResponder: model, passedArgs: launcher.launchArgs)
8076
}

Sources/SwiftCurrent_SwiftUI/Views/WorkflowView.swift

+13-11
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public struct WorkflowView<Content: View>: View {
7878
*/
7979
public init<WI: _WorkflowItemProtocol>(isLaunched: Binding<Bool> = .constant(true),
8080
@WorkflowBuilder content: () -> WI) where Content == WorkflowLauncher<WI>, WI.FlowRepresentableType.WorkflowInput == Never {
81-
self.init(isLaunched: isLaunched, startingArgs: .none, content: content())
81+
self.init(isLaunched: isLaunched, startingArgs: .none, content: content)
8282
}
8383

8484
/**
@@ -90,7 +90,7 @@ public struct WorkflowView<Content: View>: View {
9090
public init<WI: _WorkflowItemProtocol>(isLaunched: Binding<Bool> = .constant(true),
9191
launchingWith args: WI.FlowRepresentableType.WorkflowInput,
9292
@WorkflowBuilder content: () -> WI) where Content == WorkflowLauncher<WI> {
93-
self.init(isLaunched: isLaunched, startingArgs: .args(args), content: content())
93+
self.init(isLaunched: isLaunched, startingArgs: .args(args), content: content)
9494
}
9595

9696
/**
@@ -102,7 +102,7 @@ public struct WorkflowView<Content: View>: View {
102102
public init<WI: _WorkflowItemProtocol>(isLaunched: Binding<Bool> = .constant(true),
103103
launchingWith args: AnyWorkflow.PassedArgs,
104104
@WorkflowBuilder content: () -> WI) where Content == WorkflowLauncher<WI>, WI.FlowRepresentableType.WorkflowInput == AnyWorkflow.PassedArgs {
105-
self.init(isLaunched: isLaunched, startingArgs: args, content: content())
105+
self.init(isLaunched: isLaunched, startingArgs: args, content: content)
106106
}
107107

108108
/**
@@ -114,7 +114,7 @@ public struct WorkflowView<Content: View>: View {
114114
public init<WI: _WorkflowItemProtocol>(isLaunched: Binding<Bool> = .constant(true),
115115
launchingWith args: AnyWorkflow.PassedArgs,
116116
@WorkflowBuilder content: () -> WI) where Content == WorkflowLauncher<WI> {
117-
self.init(isLaunched: isLaunched, startingArgs: args, content: content())
117+
self.init(isLaunched: isLaunched, startingArgs: args, content: content)
118118
}
119119

120120
/**
@@ -126,7 +126,7 @@ public struct WorkflowView<Content: View>: View {
126126
public init<A, WI: _WorkflowItemProtocol>(isLaunched: Binding<Bool> = .constant(true),
127127
launchingWith args: A,
128128
@WorkflowBuilder content: () -> WI) where Content == WorkflowLauncher<WI>, WI.FlowRepresentableType.WorkflowInput == AnyWorkflow.PassedArgs {
129-
self.init(isLaunched: isLaunched, startingArgs: .args(args), content: content())
129+
self.init(isLaunched: isLaunched, startingArgs: .args(args), content: content)
130130
}
131131

132132
/**
@@ -138,7 +138,7 @@ public struct WorkflowView<Content: View>: View {
138138
public init<A, WI: _WorkflowItemProtocol>(isLaunched: Binding<Bool> = .constant(true),
139139
launchingWith args: A,
140140
@WorkflowBuilder content: () -> WI) where Content == WorkflowLauncher<WI>, WI.FlowRepresentableType.WorkflowInput == Never {
141-
self.init(isLaunched: isLaunched, startingArgs: .args(args), content: content())
141+
self.init(isLaunched: isLaunched, startingArgs: .args(args), content: content)
142142
}
143143

144144
/**
@@ -148,7 +148,7 @@ public struct WorkflowView<Content: View>: View {
148148
*/
149149
public init<WI: _WorkflowItemProtocol>(isLaunched: Binding<Bool> = .constant(true),
150150
@WorkflowBuilder content: () -> WI) where Content == WorkflowLauncher<WI>, WI.FlowRepresentableType.WorkflowInput == AnyWorkflow.PassedArgs {
151-
self.init(isLaunched: isLaunched, startingArgs: .none, content: content())
151+
self.init(isLaunched: isLaunched, startingArgs: .none, content: content)
152152
}
153153

154154
/**
@@ -157,7 +157,9 @@ public struct WorkflowView<Content: View>: View {
157157
- Parameter startingArgs: arguments passed to the first loaded `FlowRepresentable` in the underlying `Workflow`.
158158
- Parameter workflow: workflow to be launched; must contain `FlowRepresentable`s of type `View`
159159
*/
160-
public init(isLaunched: Binding<Bool> = .constant(true), launchingWith startingArgs: AnyWorkflow.PassedArgs = .none, workflow: AnyWorkflow) where Content == WorkflowLauncher<AnyWorkflowItem> {
160+
public init(isLaunched: Binding<Bool> = .constant(true),
161+
launchingWith startingArgs: AnyWorkflow.PassedArgs = .none,
162+
workflow: AnyWorkflow) where Content == WorkflowLauncher<WorkflowItemWrapper<AnyWorkflowItem, Never>> {
161163
workflow.forEach {
162164
assert($0.value.metadata is ExtendedFlowRepresentableMetadata, "It is possible the workflow was constructed incorrectly. This represents an internal error, please file a bug at https://github.com/wwt/SwiftCurrent/issues") // swiftlint:disable:this line_length
163165
}
@@ -171,7 +173,7 @@ public struct WorkflowView<Content: View>: View {
171173
- Parameter startingArgs: arguments passed to the first loaded `FlowRepresentable` in the underlying `Workflow`.
172174
- Parameter workflow: workflow to be launched; must contain `FlowRepresentable`s of type `View`
173175
*/
174-
public init<A>(isLaunched: Binding<Bool> = .constant(true), launchingWith startingArgs: A, workflow: AnyWorkflow) where Content == WorkflowLauncher<AnyWorkflowItem> {
176+
public init<A>(isLaunched: Binding<Bool> = .constant(true), launchingWith startingArgs: A, workflow: AnyWorkflow) where Content == WorkflowLauncher<WorkflowItemWrapper<AnyWorkflowItem, Never>> {
175177
workflow.forEach {
176178
assert($0.value.metadata is ExtendedFlowRepresentableMetadata, "It is possible the workflow was constructed incorrectly. This represents an internal error, please file a bug at https://github.com/wwt/SwiftCurrent/issues") // swiftlint:disable:this line_length
177179
}
@@ -181,8 +183,8 @@ public struct WorkflowView<Content: View>: View {
181183

182184
private init<WI: _WorkflowItemProtocol>(isLaunched: Binding<Bool>,
183185
startingArgs: AnyWorkflow.PassedArgs,
184-
content: WI) where Content == WorkflowLauncher<WI> {
185-
_content = State(wrappedValue: WorkflowLauncher(isLaunched: isLaunched, startingArgs: startingArgs) { content })
186+
@WorkflowBuilder content: () -> WI) where Content == WorkflowLauncher<WI> {
187+
_content = State(wrappedValue: WorkflowLauncher(isLaunched: isLaunched, startingArgs: startingArgs, content: content))
186188
}
187189

188190
private init<WI: _WorkflowItemProtocol>(_ other: WorkflowView<Content>,

SwiftCurrent.xcworkspace/xcshareddata/swiftpm/Package.resolved

+3-3
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
},
5858
{
5959
"package": "swift-algorithms",
60-
"repositoryURL": "https://github.com/apple/swift-algorithms",
60+
"repositoryURL": "https://github.com/apple/swift-algorithms.git",
6161
"state": {
6262
"branch": null,
6363
"revision": "b14b7f4c528c942f121c8b860b9410b2bf57825e",
@@ -87,8 +87,8 @@
8787
"repositoryURL": "https://github.com/apple/swift-syntax.git",
8888
"state": {
8989
"branch": null,
90-
"revision": "75e60475d9d8fd5bbc16a12e0eaa2cb01b0c322e",
91-
"version": "0.50500.0"
90+
"revision": "0b6c22b97f8e9320bca62e82cdbee601cf37ad3f",
91+
"version": "0.50600.1"
9292
}
9393
},
9494
{

Tests/SwiftCurrent_SwiftUITests/SwiftCurrent_NavigationLinkTests.swift

+57-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
3333
.onFinish { _ in
3434
expectOnFinish.fulfill()
3535
}
36+
.embedInNavigationView()
3637
}
3738
.hostAndInspect(with: \.inspection)
3839
.extractWorkflowLauncher()
@@ -71,6 +72,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
7172
.onFinish { _ in
7273
expectOnFinish.fulfill()
7374
}
75+
.embedInNavigationView()
7476
}
7577
.hostAndInspect(with: \.inspection)
7678
.extractWorkflowLauncher()
@@ -109,6 +111,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
109111
.onFinish { _ in
110112
expectOnFinish.fulfill()
111113
}
114+
.embedInNavigationView()
112115
}
113116
.hostAndInspect(with: \.inspection)
114117
.extractWorkflowLauncher()
@@ -152,6 +155,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
152155
.onFinish { _ in
153156
expectOnFinish.fulfill()
154157
}
158+
.embedInNavigationView()
155159
}
156160
.hostAndInspect(with: \.inspection)
157161
.extractWorkflowLauncher()
@@ -196,6 +200,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
196200
.onFinish { _ in
197201
expectOnFinish.fulfill()
198202
}
203+
.embedInNavigationView()
199204
}
200205
.hostAndInspect(with: \.inspection)
201206
.extractWorkflowLauncher()
@@ -240,6 +245,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
240245
.onFinish { _ in
241246
expectOnFinish.fulfill()
242247
}
248+
.embedInNavigationView()
243249
}
244250
.hostAndInspect(with: \.inspection)
245251
.extractWorkflowLauncher()
@@ -269,7 +275,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
269275
WorkflowItem(FR1.self).presentationType(.navigationLink)
270276
WorkflowItem(FR1.self).presentationType(.navigationLink)
271277
WorkflowItem(FR1.self)
272-
}
278+
}.embedInNavigationView()
273279
}
274280
.hostAndInspect(with: \.inspection)
275281
.extractWorkflowLauncher()
@@ -337,7 +343,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
337343
WorkflowItem(FR5.self).presentationType(.navigationLink)
338344
WorkflowItem(FR6.self).presentationType(.navigationLink)
339345
WorkflowItem(FR7.self).presentationType(.navigationLink)
340-
}
346+
}.embedInNavigationView()
341347
}
342348
.hostAndInspect(with: \.inspection)
343349
.extractWorkflowLauncher()
@@ -364,6 +370,51 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
364370
try await wfr7.find(FR7.self).proceedInWorkflow()
365371
}
366372

373+
func testWorkflowCanBeAbandoned() async throws {
374+
struct FR1: View, FlowRepresentable, Inspectable {
375+
var _workflowPointer: AnyFlowRepresentable?
376+
var body: some View { Text("FR1 type") }
377+
}
378+
struct FR2: View, FlowRepresentable, Inspectable {
379+
var _workflowPointer: AnyFlowRepresentable?
380+
var body: some View { Text("FR2 type") }
381+
}
382+
struct FR3: View, FlowRepresentable, Inspectable {
383+
var _workflowPointer: AnyFlowRepresentable?
384+
var body: some View {
385+
Button("continue") {
386+
workflow?.abandon()
387+
}
388+
}
389+
}
390+
391+
let wfr1 = try await MainActor.run {
392+
WorkflowView {
393+
WorkflowItem(FR1.self).presentationType(.navigationLink)
394+
WorkflowItem(FR2.self).presentationType(.navigationLink)
395+
WorkflowItem(FR3.self).presentationType(.navigationLink)
396+
}.embedInNavigationView()
397+
}
398+
.hostAndInspect(with: \.inspection)
399+
.extractWorkflowLauncher()
400+
.extractWorkflowItemWrapper()
401+
402+
let navLink = try XCTUnwrap(try? wfr1.find(ViewType.NavigationLink.self))
403+
try await wfr1.proceedAndCheckNavLink(on: FR1.self)
404+
XCTAssertFalse(try navLink.isActive())
405+
406+
let wfr2 = try await wfr1.extractWrappedWrapper()
407+
try await wfr2.proceedAndCheckNavLink(on: FR2.self)
408+
409+
let wfr3 = try await wfr2.extractWrappedWrapper()
410+
XCTAssertNoThrow(try wfr3.find(button: "continue").tap())
411+
412+
XCTAssertNoThrow(try wfr1.find(FR1.self))
413+
XCTAssertEqual(try wfr1.find(ViewType.NavigationLink.self).isActive(), false)
414+
XCTAssertThrowsError(try wfr1.find(FR2.self))
415+
XCTAssertThrowsError(try wfr1.find(FR3.self))
416+
}
417+
367418
func testNavLinkWorkflowsCanSkipTheFirstItem() async throws {
368419
struct FR1: View, FlowRepresentable, Inspectable {
369420
var _workflowPointer: AnyFlowRepresentable?
@@ -384,6 +435,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
384435
WorkflowItem(FR2.self).presentationType(.navigationLink)
385436
WorkflowItem(FR3.self)
386437
}
438+
.embedInNavigationView()
387439
}
388440
.hostAndInspect(with: \.inspection)
389441
.extractWorkflowLauncher()
@@ -418,6 +470,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
418470
WorkflowItem(FR2.self).presentationType(.navigationLink)
419471
WorkflowItem(FR3.self)
420472
}
473+
.embedInNavigationView()
421474
}
422475
.hostAndInspect(with: \.inspection)
423476
.extractWorkflowLauncher()
@@ -458,6 +511,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
458511
WorkflowItem(FR3.self)
459512
WorkflowItem(FR4.self)
460513
}
514+
.embedInNavigationView()
461515
}
462516
.hostAndInspect(with: \.inspection)
463517
.extractWorkflowLauncher()
@@ -500,6 +554,7 @@ final class SwiftCurrent_NavigationLinkTests: XCTestCase, View {
500554
.onFinish { _ in
501555
expectOnFinish.fulfill()
502556
}
557+
.embedInNavigationView()
503558
}
504559
.hostAndInspect(with: \.inspection)
505560
.extractWorkflowLauncher()

0 commit comments

Comments
 (0)