From de759106af281c06cae9c595bae5315fbbdbd1a8 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Tue, 26 Oct 2021 10:48:52 -0600 Subject: [PATCH 01/17] Create WorkflowCombine library --- Package.swift | 9 +++++++ WorkflowCombine.podspec | 24 +++++++++++++++++++ .../Sources/WorkflowPublisher.swift | 21 ++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 WorkflowCombine.podspec create mode 100644 WorkflowCombine/Sources/WorkflowPublisher.swift diff --git a/Package.swift b/Package.swift index 304565406..21dd18027 100644 --- a/Package.swift +++ b/Package.swift @@ -26,6 +26,10 @@ let package = Package( name: "WorkflowReactiveSwift", targets: ["WorkflowReactiveSwift"] ), + .library( + name: "WorkflowCombine", + targets: ["WorkflowCombine"] + ), ], dependencies: [ .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift.git", from: "6.3.0"), @@ -62,6 +66,11 @@ let package = Package( dependencies: ["ReactiveSwift", "Workflow"], path: "WorkflowReactiveSwift/Sources" ), + .target( + name: "WorkflowCombine", + dependencies: ["Workflow"], + path: "WorkflowCombine/Sources" + ), ], swiftLanguageVersions: [.v5] ) diff --git a/WorkflowCombine.podspec b/WorkflowCombine.podspec new file mode 100644 index 000000000..6f266626e --- /dev/null +++ b/WorkflowCombine.podspec @@ -0,0 +1,24 @@ +require_relative('version') + +Pod::Spec.new do |s| + s.name = 'WorkflowCombine' + s.version = WORKFLOW_VERSION + s.summary = 'Infrastructure for Workflow-powered Combine' + s.homepage = 'https://www.github.com/square/workflow-swift' + s.license = 'Apache License, Version 2.0' + s.author = 'Square' + s.source = { :git => 'https://github.com/square/workflow-swift.git', :tag => "v#{s.version}" } + + # 1.7 is needed for `swift_versions` support + s.cocoapods_version = '>= 1.7.0' + + s.swift_versions = ['5.1'] + s.ios.deployment_target = '13.0' + s.osx.deployment_target = '10.15' + + s.source_files = 'WorkflowCombine/Sources/*.swift' + + s.dependency 'Workflow', "#{s.version}" + + end + diff --git a/WorkflowCombine/Sources/WorkflowPublisher.swift b/WorkflowCombine/Sources/WorkflowPublisher.swift new file mode 100644 index 000000000..de028cda6 --- /dev/null +++ b/WorkflowCombine/Sources/WorkflowPublisher.swift @@ -0,0 +1,21 @@ +/* + * Copyright 2021 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if canImport(Combine) && swift(>=5.1) + + import Combine + +#endif From decc7c020959e0a3a423896c7065fcda886ca737 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Tue, 26 Oct 2021 14:51:31 -0600 Subject: [PATCH 02/17] Create Worker backed with Combine Publisher --- WorkflowCombine/Sources/Logger.swift | 63 +++++++++++ .../Sources/PublishedWorkflow.swift | 59 +++++++++++ WorkflowCombine/Sources/Worker.swift | 100 ++++++++++++++++++ .../Sources/WorkflowPublisher.swift | 21 ---- WorkflowRxSwift/Sources/Worker.swift | 6 +- 5 files changed, 225 insertions(+), 24 deletions(-) create mode 100644 WorkflowCombine/Sources/Logger.swift create mode 100644 WorkflowCombine/Sources/PublishedWorkflow.swift create mode 100644 WorkflowCombine/Sources/Worker.swift delete mode 100644 WorkflowCombine/Sources/WorkflowPublisher.swift diff --git a/WorkflowCombine/Sources/Logger.swift b/WorkflowCombine/Sources/Logger.swift new file mode 100644 index 000000000..1d5f83fe4 --- /dev/null +++ b/WorkflowCombine/Sources/Logger.swift @@ -0,0 +1,63 @@ +/* + * Copyright 2021 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import os.signpost + +private extension OSLog { + static let worker = OSLog(subsystem: "com.squareup.WorkflowCombine", category: "Worker") +} + +@available(iOS 13.0, macOS 10.15, *) +/// Logs Worker events to OSLog +final class WorkerLogger { + init() {} + + var signpostID: OSSignpostID { OSSignpostID(log: .worker, object: self) } + + // MARK: - Workers + + func logStarted() { + os_signpost( + .begin, + log: .worker, + name: "Running", + signpostID: signpostID, + "Worker: %{private}@", + String(describing: WorkerType.self) + ) + } + + func logFinished(status: StaticString) { + os_signpost( + .end, + log: .worker, + name: "Running", + signpostID: signpostID, + status + ) + } + + func logOutput() { + os_signpost( + .event, + log: .worker, + name: "Worker Event", + signpostID: signpostID, + "Event: %{private}@", + String(describing: WorkerType.self) + ) + } +} diff --git a/WorkflowCombine/Sources/PublishedWorkflow.swift b/WorkflowCombine/Sources/PublishedWorkflow.swift new file mode 100644 index 000000000..a79be74bf --- /dev/null +++ b/WorkflowCombine/Sources/PublishedWorkflow.swift @@ -0,0 +1,59 @@ +/* + * Copyright 2021 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if canImport(Combine) && swift(>=5.1) + + import Combine + import Foundation + import Workflow + + @available(iOS 13.0, macOS 10.15, *) + extension AnyPublisher: AnyWorkflowConvertible where Failure == Never { + public func asAnyWorkflow() -> AnyWorkflow { + return PublishedWorkflow(publisher: self).asAnyWorkflow() + } + } + + @available(iOS 13.0, macOS 10.15, *) + struct PublishedWorkflow: Workflow { + public typealias Output = Value + public typealias State = Void + public typealias Rendering = Void + + let publisher: AnyPublisher + + public init(publisher: AnyPublisher) { + self.publisher = publisher + } + + public func render(state: State, context: RenderContext) -> Rendering { + let sink = context.makeSink(of: AnyWorkflowAction.self) + context.runSideEffect(key: "") { [publisher] lifetime in + let cancellable = publisher + .map { AnyWorkflowAction(sendingOutput: $0) } + .subscribe(on: RunLoop.main) + .sink(receiveValue: { value in + sink.send(value) + }) + + lifetime.onEnded { + cancellable.cancel() + } + } + } + } + +#endif diff --git a/WorkflowCombine/Sources/Worker.swift b/WorkflowCombine/Sources/Worker.swift new file mode 100644 index 000000000..4c0b8274c --- /dev/null +++ b/WorkflowCombine/Sources/Worker.swift @@ -0,0 +1,100 @@ +/* + * Copyright 2021 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if canImport(Combine) && swift(>=5.1) + + import Combine + import Foundation + import Workflow + + /// Workers define a unit of asynchronous work. + /// + /// 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. + /// + /// If there is not an existing worker of this type, the context will kick off the new worker (via `run`). + @available(iOS 13.0, macOS 10.15, *) + public protocol Worker: AnyWorkflowConvertible where Rendering == Void { + /// The type of output events returned by this worker. + associatedtype Output + + /// Returns a publisher to execute the work represented by this worker. + func run() -> Just + + /// Returns `true` if the other worker should be considered equivalent to `self`. Equivalence should take into + /// account whatever data is meaningful to the task. For example, a worker that loads a user account from a server + /// would not be equivalent to another worker with a different user ID. + func isEquivalent(to otherWorker: Self) -> Bool + } + + @available(iOS 13.0, macOS 10.15, *) + extension Worker { + public func asAnyWorkflow() -> AnyWorkflow { + WorkerWorkflow(worker: self).asAnyWorkflow() + } + } + + @available(iOS 13.0, macOS 10.15, *) + struct WorkerWorkflow: Workflow { + let worker: WorkerType + + typealias Output = WorkerType.Output + typealias Rendering = Void + typealias State = UUID + + func makeInitialState() -> State { UUID() } + + func workflowDidChange(from previousWorkflow: WorkerWorkflow, state: inout UUID) { + if !worker.isEquivalent(to: previousWorkflow.worker) { + state = UUID() + } + } + + func render(state: State, context: RenderContext) -> Rendering { + let logger = WorkerLogger() + + worker.run() + .handleEvents( + receiveSubscription: { _ in + logger.logStarted() + }, + receiveOutput: { output in + logger.logOutput() + }, + receiveCompletion: { completion in + // no need to switch completion since Failure is hardcoded to Never + logger.logFinished(status: "Finished") + }, + receiveCancel: { + logger.logFinished(status: "Cancelled") + } + ) + .map { AnyWorkflowAction(sendingOutput: $0) } + .eraseToAnyPublisher() + .running(in: context, key: state.uuidString) + } + } + + @available(iOS 13.0, macOS 10.15, *) + extension Worker where Self: Equatable { + public func isEquivalent(to otherWorker: Self) -> Bool { + self == otherWorker + } + } + +#endif diff --git a/WorkflowCombine/Sources/WorkflowPublisher.swift b/WorkflowCombine/Sources/WorkflowPublisher.swift deleted file mode 100644 index de028cda6..000000000 --- a/WorkflowCombine/Sources/WorkflowPublisher.swift +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2021 Square Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if canImport(Combine) && swift(>=5.1) - - import Combine - -#endif diff --git a/WorkflowRxSwift/Sources/Worker.swift b/WorkflowRxSwift/Sources/Worker.swift index ccf6819dc..e92c44852 100644 --- a/WorkflowRxSwift/Sources/Worker.swift +++ b/WorkflowRxSwift/Sources/Worker.swift @@ -56,17 +56,17 @@ struct WorkerWorkflow: Workflow { UUID() } - func workflowDidChange(from previousWorkflow: WorkerWorkflow, state: inout UUID) { + func workflowDidChange(from previousWorkflow: Self, state: inout UUID) { if !worker.isEquivalent(to: previousWorkflow.worker) { state = UUID() } } - func render(state: State, context: RenderContext) -> Rendering { + func render(state: State, context: RenderContext) -> Rendering { let logger = WorkerLogger() worker.run() - .map { AnyWorkflowAction(sendingOutput: $0) } + .map { AnyWorkflowAction(sendingOutput: $0) } .do( onNext: { _ in logger.logOutput() From b06a710ce10993c67523071245b55390398c7dd2 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Tue, 26 Oct 2021 15:16:14 -0600 Subject: [PATCH 03/17] Add Testing modules and podspec --- WorkflowCombine.podspec | 2 +- WorkflowCombineTesting.podspec | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 WorkflowCombineTesting.podspec diff --git a/WorkflowCombine.podspec b/WorkflowCombine.podspec index 6f266626e..7ee44c369 100644 --- a/WorkflowCombine.podspec +++ b/WorkflowCombine.podspec @@ -3,7 +3,7 @@ require_relative('version') Pod::Spec.new do |s| s.name = 'WorkflowCombine' s.version = WORKFLOW_VERSION - s.summary = 'Infrastructure for Workflow-powered Combine' + s.summary = 'Infrastructure for Combined-powered Workers' s.homepage = 'https://www.github.com/square/workflow-swift' s.license = 'Apache License, Version 2.0' s.author = 'Square' diff --git a/WorkflowCombineTesting.podspec b/WorkflowCombineTesting.podspec new file mode 100644 index 000000000..b8e8fa580 --- /dev/null +++ b/WorkflowCombineTesting.podspec @@ -0,0 +1,34 @@ +require_relative('version') + +Pod::Spec.new do |s| + s.name = 'WorkflowCombineTesting' + s.version = WORKFLOW_VERSION + s.summary = 'Infrastructure for Combined-powered Workers' + s.homepage = 'https://www.github.com/square/workflow-swift' + s.license = 'Apache License, Version 2.0' + s.author = 'Square' + s.source = { :git => 'https://github.com/square/workflow-swift.git', :tag => "v#{s.version}" } + + # 1.7 is needed for `swift_versions` support + s.cocoapods_version = '>= 1.7.0' + + s.swift_versions = ['5.1'] + s.ios.deployment_target = '13.0' + s.osx.deployment_target = '10.15' + + s.source_files = 'WorkflowCombine/Testing/**/*.swift' + + s.dependency 'Workflow', "#{s.version}" + s.dependency 'WorkflowCombine', "#{s.version}" + s.dependency 'WorkflowTesting', "#{s.version}" + + s.framework = 'XCTest' + + s.test_spec 'WorkflowCombineTestingTests' do |test_spec| + test_spec.requires_app_host = true + test_spec.source_files = 'WorkflowCombine/TestingTests/**/*.swift' + test_spec.framework = 'XCTest' + test_spec.dependency 'WorkflowTesting' + test_spec.library = 'swiftos' + end +end From 41a3c577924d2d71630c6b5a9352606f3c089c2e Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Wed, 27 Oct 2021 16:47:45 -0600 Subject: [PATCH 04/17] Completed same test suite as the WorkflowReactiveSwift library --- Development.podspec | 19 ++ Package.swift | 10 + ...Workflow.swift => PublisherWorkflow.swift} | 14 +- WorkflowCombine/Sources/Worker.swift | 42 ++-- WorkflowCombine/Tests/AnyPublisherTests.swift | 41 ++++ .../Tests/Helpers/AnyPublisherTesting.swift | 47 ++++ .../Tests/Helpers/WorkerTesting.swift | 56 +++++ WorkflowCombine/Tests/TestingTests.swift | 195 +++++++++++++++++ WorkflowCombine/Tests/WorkerTests.swift | 205 ++++++++++++++++++ WorkflowRxSwift/Sources/Worker.swift | 6 +- 10 files changed, 602 insertions(+), 33 deletions(-) rename WorkflowCombine/Sources/{PublishedWorkflow.swift => PublisherWorkflow.swift} (81%) create mode 100644 WorkflowCombine/Tests/AnyPublisherTests.swift create mode 100644 WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift create mode 100644 WorkflowCombine/Tests/Helpers/WorkerTesting.swift create mode 100644 WorkflowCombine/Tests/TestingTests.swift create mode 100644 WorkflowCombine/Tests/WorkerTests.swift diff --git a/Development.podspec b/Development.podspec index 274cf3b08..34e3cd29d 100644 --- a/Development.podspec +++ b/Development.podspec @@ -13,6 +13,7 @@ Pod::Spec.new do |s| s.dependency 'WorkflowUI' s.dependency 'WorkflowReactiveSwift' s.dependency 'WorkflowRxSwift' + # s.dependency 'WorkflowCombine' # TODO: Disabled because app specs cannot increase the deployment target of the root s.source_files = 'Samples/Dummy.swift' s.subspec 'Dummy' do |ss| @@ -141,4 +142,22 @@ Pod::Spec.new do |s| test_spec.dependency 'WorkflowTesting' test_spec.dependency 'WorkflowRxSwiftTesting' end + + # TODO: Disabled because app specs cannot increase the deployment target of the root + # To use, increase the deployment target of this spec to 13.0 or higher + # s.test_spec 'WorkflowCombineTests' do |test_spec| + # test_spec.requires_app_host = true + # test_spec.source_files = 'WorkflowCombine/Tests/**/*.swift' + # test_spec.framework = 'XCTest' + # test_spec.dependency 'WorkflowTesting' + # test_spec.dependency 'WorkflowCombineTesting' + # end + + # s.test_spec 'WorkflowCombineTestingTests' do |test_spec| + # test_spec.requires_app_host = true + # test_spec.source_files = 'WorkflowCombine/TestingTests/**/*.swift' + # test_spec.framework = 'XCTest' + # test_spec.dependency 'WorkflowTesting' + # test_spec.dependency 'WorkflowCombineTesting' + # end end diff --git a/Package.swift b/Package.swift index 21dd18027..73615c6b9 100644 --- a/Package.swift +++ b/Package.swift @@ -71,6 +71,16 @@ let package = Package( dependencies: ["Workflow"], path: "WorkflowCombine/Sources" ), + .testTarget( + name: "WorkflowCombineTests", + dependencies: ["Workflow", "WorkflowCombine", "WorkflowTesting"], + path: "WorkflowCombine/Tests" + ), + .target( + name: "WorkflowTesting", + dependencies: ["Workflow"], + path: "WorkflowTesting/Sources" + ), ], swiftLanguageVersions: [.v5] ) diff --git a/WorkflowCombine/Sources/PublishedWorkflow.swift b/WorkflowCombine/Sources/PublisherWorkflow.swift similarity index 81% rename from WorkflowCombine/Sources/PublishedWorkflow.swift rename to WorkflowCombine/Sources/PublisherWorkflow.swift index a79be74bf..f244d1511 100644 --- a/WorkflowCombine/Sources/PublishedWorkflow.swift +++ b/WorkflowCombine/Sources/PublisherWorkflow.swift @@ -23,12 +23,12 @@ @available(iOS 13.0, macOS 10.15, *) extension AnyPublisher: AnyWorkflowConvertible where Failure == Never { public func asAnyWorkflow() -> AnyWorkflow { - return PublishedWorkflow(publisher: self).asAnyWorkflow() + return PublisherWorkflow(publisher: self).asAnyWorkflow() } } @available(iOS 13.0, macOS 10.15, *) - struct PublishedWorkflow: Workflow { + struct PublisherWorkflow: Workflow { public typealias Output = Value public typealias State = Void public typealias Rendering = Void @@ -42,16 +42,10 @@ public func render(state: State, context: RenderContext) -> Rendering { let sink = context.makeSink(of: AnyWorkflowAction.self) context.runSideEffect(key: "") { [publisher] lifetime in - let cancellable = publisher + _ = publisher .map { AnyWorkflowAction(sendingOutput: $0) } .subscribe(on: RunLoop.main) - .sink(receiveValue: { value in - sink.send(value) - }) - - lifetime.onEnded { - cancellable.cancel() - } + .sink { sink.send($0) } } } } diff --git a/WorkflowCombine/Sources/Worker.swift b/WorkflowCombine/Sources/Worker.swift index 4c0b8274c..930d023f0 100644 --- a/WorkflowCombine/Sources/Worker.swift +++ b/WorkflowCombine/Sources/Worker.swift @@ -34,7 +34,7 @@ associatedtype Output /// Returns a publisher to execute the work represented by this worker. - func run() -> Just + func run() -> AnyPublisher /// Returns `true` if the other worker should be considered equivalent to `self`. Equivalence should take into /// account whatever data is meaningful to the task. For example, a worker that loads a user account from a server @@ -68,25 +68,27 @@ func render(state: State, context: RenderContext) -> Rendering { let logger = WorkerLogger() - worker.run() - .handleEvents( - receiveSubscription: { _ in - logger.logStarted() - }, - receiveOutput: { output in - logger.logOutput() - }, - receiveCompletion: { completion in - // no need to switch completion since Failure is hardcoded to Never - logger.logFinished(status: "Finished") - }, - receiveCancel: { - logger.logFinished(status: "Cancelled") - } - ) - .map { AnyWorkflowAction(sendingOutput: $0) } - .eraseToAnyPublisher() - .running(in: context, key: state.uuidString) + Deferred { + worker.run() + .handleEvents( + receiveSubscription: { _ in + logger.logStarted() + }, + receiveOutput: { output in + logger.logOutput() + }, + receiveCompletion: { completion in + // no need to switch completion since Failure is hardcoded to Never + logger.logFinished(status: "Finished") + }, + receiveCancel: { + logger.logFinished(status: "Cancelled") + } + ) + .map { AnyWorkflowAction(sendingOutput: $0) } + } + .eraseToAnyPublisher() + .running(in: context, key: state.uuidString) } } diff --git a/WorkflowCombine/Tests/AnyPublisherTests.swift b/WorkflowCombine/Tests/AnyPublisherTests.swift new file mode 100644 index 000000000..faf4450ba --- /dev/null +++ b/WorkflowCombine/Tests/AnyPublisherTests.swift @@ -0,0 +1,41 @@ +/* + * Copyright 2021 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Combine +import Foundation +import Workflow +import XCTest + +class AnyPublisherTests: XCTestCase { + func testPublisherWorkflow() { + TestWorkflow() + .renderTester() + .expectPublisher(producingOutput: 1, key: "123") + .render {} + } + + struct TestWorkflow: Workflow { + typealias State = Void + typealias Rendering = Void + + func render(state: State, context: RenderContext) -> Rendering { + Just(1) + .map { _ in AnyWorkflowAction.noAction } + .eraseToAnyPublisher() + .running(in: context, key: "123") + } + } +} diff --git a/WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift b/WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift new file mode 100644 index 000000000..788e4585f --- /dev/null +++ b/WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift @@ -0,0 +1,47 @@ +/* + * Copyright 2021 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if DEBUG + + import Workflow + import WorkflowTesting + import XCTest + @testable import WorkflowCombine + + extension RenderTester { + /// Expect a `Publisher`s. + /// + /// `PublisherWorkflow` is used to subscribe to `Publisher`s. + /// + /// - Parameters: + /// - producingOutput: An output that should be returned when this worker is requested, if any. + /// - key: Key to expect this `Workflow` to be rendered with. + public func expectPublisher( + producingOutput output: OutputType? = nil, + key: String = "", + file: StaticString = #file, line: UInt = #line + ) -> RenderTester { + expectWorkflow( + type: PublisherWorkflow.self, + key: key, + producingRendering: (), + producingOutput: output, + assertions: { _ in } + ) + } + } + +#endif diff --git a/WorkflowCombine/Tests/Helpers/WorkerTesting.swift b/WorkflowCombine/Tests/Helpers/WorkerTesting.swift new file mode 100644 index 000000000..92dfcbc20 --- /dev/null +++ b/WorkflowCombine/Tests/Helpers/WorkerTesting.swift @@ -0,0 +1,56 @@ +/* + * Copyright 2020 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if DEBUG + + import Workflow + import WorkflowTesting + import XCTest + @testable import WorkflowCombine + + extension RenderTester { + /// Expect the given worker. It will be checked for `isEquivalent(to:)` with the requested worker. + /// + /// - Parameters: + /// - worker: The worker to be expected + /// - producingOutput: An output that should be returned when this worker is requested, if any. + /// - key: Key to expect this `Workflow` to be rendered with. + public func expect( + worker: ExpectedWorkerType, + producingOutput output: ExpectedWorkerType.Output? = nil, + key: String = "", + file: StaticString = #file, line: UInt = #line + ) -> RenderTester { + expectWorkflow( + type: WorkerWorkflow.self, + key: key, + producingRendering: (), + producingOutput: output, + assertions: { workflow in + guard !workflow.worker.isEquivalent(to: worker) else { + return + } + XCTFail( + "Workers of type \(ExpectedWorkerType.self) not equivalent. Expected: \(worker). Got: \(workflow.worker)", + file: file, + line: line + ) + } + ) + } + } + +#endif diff --git a/WorkflowCombine/Tests/TestingTests.swift b/WorkflowCombine/Tests/TestingTests.swift new file mode 100644 index 000000000..25ca6526a --- /dev/null +++ b/WorkflowCombine/Tests/TestingTests.swift @@ -0,0 +1,195 @@ +/* + * Copyright 2020 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Combine +import Workflow +import WorkflowCombine +import WorkflowTesting +import XCTest + +class WorkflowCombineTestingTests: XCTestCase { + func test_workers() { + let renderTester = TestWorkflow() + .renderTester(initialState: .init(mode: .worker(input: "otherText"), output: "")) + + renderTester + .expect(worker: TestWorker(input: "otherText")) + .render { _ in } + } + + func test_workerOutput_updatesState() { + let renderTester = TestWorkflow() + .renderTester(initialState: .init(mode: .worker(input: "otherText"), output: "")) + + renderTester + .expect( + worker: TestWorker(input: "otherText"), + producingOutput: "otherText" + ) + .render { _ in } + .verifyState { state in + XCTAssertEqual(state, TestWorkflow.State(mode: .worker(input: "otherText"), output: "otherText")) + } + } + + func test_worker_missing() { + let tester = TestWorkflow() + .renderTester() + .expect( + worker: TestWorker(input: "input") + ) + + expectingFailure(#"Expected child workflow of type: WorkerWorkflow, key: """#) { + tester.render { _ in } + } + } + + func test_worker_mismatch() { + let tester = TestWorkflow() + .renderTester(initialState: .init(mode: .worker(input: "test"), output: "")) + .expect( + worker: TestWorker(input: "not-test") + ) + + expectingFailures([ + #"Workers of type TestWorker not equivalent. Expected: TestWorker(input: "not-test"). Got: TestWorker(input: "test")"#, + ]) { + tester.render { _ in } + } + } + + func test_worker_unexpected() { + let tester = TestWorkflow() + .renderTester(initialState: .init(mode: .worker(input: "test"), output: "")) + + expectingFailure(#"Unexpected workflow of type WorkerWorkflow with key """#) { + tester.render { _ in } + } + } + + // MARK: - Failure Recording + + var expectedFailureStrings: [String] = [] + + @discardableResult + func expectingFailure( + _ messageSubstring: String, + file: StaticString = #file, line: UInt = #line, + perform: () -> Result + ) -> Result { + return expectingFailures([messageSubstring], file: file, line: line, perform: perform) + } + + @discardableResult + func expectingFailures( + _ messageSubstrings: [String], + file: StaticString = #file, line: UInt = #line, + perform: () -> Result + ) -> Result { + expectedFailureStrings = messageSubstrings + let result = perform() + if !expectedFailureStrings.isEmpty { + let leftOverExpectedFailures = expectedFailureStrings + expectedFailureStrings = [] + for failure in leftOverExpectedFailures { + XCTFail(#"Expected failure matching "\#(failure)""#, file: file, line: line) + } + } + return result + } + + /// Check for the given failure description and remove it if there’s a matching expected failure + /// - Parameter description: The failure description to check & remove + /// - Returns: `true` if the failure was expected and removed, otherwise `false` + private func removeFailure(withDescription description: String) -> Bool { + if let matchedIndex = expectedFailureStrings.firstIndex(where: { description.contains($0) }) { + expectedFailureStrings.remove(at: matchedIndex) + return true + } else { + return false + } + } + + #if swift(>=5.3) + + // Undeprecated API on Xcode 12+ (which ships with Swift 5.3) + override func record(_ issue: XCTIssue) { + if removeFailure(withDescription: issue.compactDescription) { + // Don’t forward the issue, it was expected + } else { + super.record(issue) + } + } + + #else + + // Otherwise, use old API + override func recordFailure(withDescription description: String, inFile filePath: String, atLine lineNumber: Int, expected: Bool) { + if removeFailure(withDescription: description) { + // Don’t forward the failure, it was expected + } else { + super.recordFailure(withDescription: description, inFile: filePath, atLine: lineNumber, expected: expected) + } + } + + #endif +} + +private struct TestWorkflow: Workflow { + struct State: Equatable { + enum Mode: Equatable { + case idle + case worker(input: String) + } + + let mode: Mode + var output: String + } + + func makeInitialState() -> State { + .init(mode: .idle, output: "") + } + + func workflowDidChange(from previousWorkflow: TestWorkflow, state: inout State) {} + + func render(state: State, context: RenderContext) { + switch state.mode { + case .idle: + break + case .worker(input: let input): + TestWorker(input: input) + .mapOutput { output in + AnyWorkflowAction { + $0.output = output + return nil + } + } + .running(in: context) + } + } +} + +private struct TestWorker: Worker { + let input: String + + func run() -> AnyPublisher { + Just("").eraseToAnyPublisher() + } + + func isEquivalent(to otherWorker: TestWorker) -> Bool { + input == otherWorker.input + } +} diff --git a/WorkflowCombine/Tests/WorkerTests.swift b/WorkflowCombine/Tests/WorkerTests.swift new file mode 100644 index 000000000..bae2720af --- /dev/null +++ b/WorkflowCombine/Tests/WorkerTests.swift @@ -0,0 +1,205 @@ +/* + * Copyright 2020 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Combine +import Workflow +import XCTest +@testable import WorkflowCombine + +class WorkerTests: XCTestCase { + func testExpectedWorker() { + PublisherTestWorkflow(key: "123") + .renderTester() + .expectWorkflow( + type: WorkerWorkflow.self, + key: "123", + producingRendering: (), + producingOutput: 1, + assertions: { _ in } + ) + .render { _ in } + .verifyState { state in + XCTAssertEqual(state, 1) + } + } + + func testWorkerOutput() { + let host = WorkflowHost( + workflow: PublisherTestWorkflow(key: "") + ) + + let expectation = XCTestExpectation() + let disposable = host.rendering.signal.observeValues { rendering in + expectation.fulfill() + } + + XCTAssertEqual(0, host.rendering.value) + + wait(for: [expectation], timeout: 1.0) + XCTAssertEqual(1, host.rendering.value) + + disposable?.dispose() + } + + // A worker declared on a first `render` pass that is not on a subsequent should have the work cancelled. + func test_cancelsWorkers() { + struct WorkerWorkflow: Workflow { + typealias State = Void + + enum Mode { + case notWorking + case working(start: XCTestExpectation, end: XCTestExpectation) + } + + let mode: Mode + + func render(state: State, context: RenderContext) -> Bool { + switch mode { + case .notWorking: + return false + case .working(start: let startExpectation, end: let endExpectation): + ExpectingWorker( + startExpectation: startExpectation, + endExpectation: endExpectation + ) + .mapOutput { _ in AnyWorkflowAction.noAction } + .running(in: context) + return true + } + } + + struct ExpectingWorker: Worker { + typealias Output = Void + + let startExpectation: XCTestExpectation + let endExpectation: XCTestExpectation + + func run() -> AnyPublisher { + Just(()) + .handleEvents( + receiveSubscription: { _ in + self.startExpectation.fulfill() + + }, + receiveCompletion: { _ in + self.endExpectation.fulfill() + } + ) + .eraseToAnyPublisher() + } + + func isEquivalent(to otherWorker: WorkerWorkflow.ExpectingWorker) -> Bool { + true + } + } + } + + let startExpectation = XCTestExpectation() + let endExpectation = XCTestExpectation() + let host = WorkflowHost( + workflow: WorkerWorkflow(mode: .working( + start: startExpectation, + end: endExpectation + )) + ) + + wait(for: [startExpectation], timeout: 1.0) + + host.update(workflow: WorkerWorkflow(mode: .notWorking)) + + wait(for: [endExpectation], timeout: 1.0) + } + + func test_handlesRepeatedWorkerOutputs() { + struct WF: Workflow { + typealias Output = Int + typealias Rendering = Void + + func render(state: Void, context: RenderContext) { + TestWorker() + .mapOutput { AnyWorkflowAction(sendingOutput: $0) } + .running(in: context) + } + } + + struct TestWorker: Worker { + private let publisher = PassthroughSubject() + + func isEquivalent(to otherWorker: TestWorker) -> Bool { + true + } + + func run() -> AnyPublisher { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + publisher.send(1) + publisher.send(2) + } + return publisher.eraseToAnyPublisher() + } + } + + let expectation = XCTestExpectation(description: "Test Worker") + + let host = WorkflowHost(workflow: WF()) + + var outputs: [Int] = [] + host.output.signal.observeValues { output in + outputs.append(output) + + if outputs.count == 2 { + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: 1.0) + + XCTAssertEqual(outputs, [1, 2]) + } +} + +private struct PublisherTestWorkflow: Workflow { + typealias State = Int + typealias Rendering = Int + + let key: String + + func makeInitialState() -> Int { + 0 + } + + func render(state: Int, context: RenderContext) -> Int { + PublisherTestWorker() + .mapOutput { output in + AnyWorkflowAction { state in + state = output + return nil + } + } + .running(in: context, key: key) + return state + } +} + +private struct PublisherTestWorker: Worker { + typealias Output = Int + func run() -> AnyPublisher { + Just(1).eraseToAnyPublisher() + } + + func isEquivalent(to otherWorker: PublisherTestWorker) -> Bool { + true + } +} diff --git a/WorkflowRxSwift/Sources/Worker.swift b/WorkflowRxSwift/Sources/Worker.swift index e92c44852..ccf6819dc 100644 --- a/WorkflowRxSwift/Sources/Worker.swift +++ b/WorkflowRxSwift/Sources/Worker.swift @@ -56,17 +56,17 @@ struct WorkerWorkflow: Workflow { UUID() } - func workflowDidChange(from previousWorkflow: Self, state: inout UUID) { + func workflowDidChange(from previousWorkflow: WorkerWorkflow, state: inout UUID) { if !worker.isEquivalent(to: previousWorkflow.worker) { state = UUID() } } - func render(state: State, context: RenderContext) -> Rendering { + func render(state: State, context: RenderContext) -> Rendering { let logger = WorkerLogger() worker.run() - .map { AnyWorkflowAction(sendingOutput: $0) } + .map { AnyWorkflowAction(sendingOutput: $0) } .do( onNext: { _ in logger.logOutput() From 262c8da2a3b1fd2eae1ffd5925457852f6399ece Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Thu, 28 Oct 2021 14:52:54 -0600 Subject: [PATCH 05/17] Add missing Publisher tests --- WorkflowCombine/Tests/AnyPublisherTests.swift | 68 +++++++++++++++++++ WorkflowCombine/Tests/WorkerTests.swift | 10 +-- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/WorkflowCombine/Tests/AnyPublisherTests.swift b/WorkflowCombine/Tests/AnyPublisherTests.swift index faf4450ba..0791193fe 100644 --- a/WorkflowCombine/Tests/AnyPublisherTests.swift +++ b/WorkflowCombine/Tests/AnyPublisherTests.swift @@ -18,6 +18,7 @@ import Combine import Foundation import Workflow import XCTest +@testable import WorkflowCombine class AnyPublisherTests: XCTestCase { func testPublisherWorkflow() { @@ -38,4 +39,71 @@ class AnyPublisherTests: XCTestCase { .running(in: context, key: "123") } } + + func test_publisherWorkflow_usesSideEffectWithKey() { + let publisher = Just(1).eraseToAnyPublisher() + PublisherWorkflow(publisher: publisher) + .renderTester() + .expectSideEffect(key: "") + .render { _ in } + } + + func test_output() { + let publisher = Just(1).eraseToAnyPublisher() + + let host = WorkflowHost( + workflow: PublisherWorkflow(publisher: publisher) + ) + + let expectation = XCTestExpectation() + var outputValue: Int? + let disposable = host.output.signal.observeValues { output in + outputValue = output + expectation.fulfill() + } + + wait(for: [expectation], timeout: 1) + XCTAssertEqual(1, outputValue) + + disposable?.dispose() + } + + func test_multipleOutputs() { + let publisher = [1, 2, 3].publisher + + let host = WorkflowHost( + workflow: PublisherWorkflow(publisher: publisher.eraseToAnyPublisher()) + ) + + let expectation = XCTestExpectation() + var outputValues = [Int]() + let disposable = host.output.signal.observeValues { output in + outputValues.append(output) + expectation.fulfill() + } + + wait(for: [expectation], timeout: 1) + XCTAssertEqual([1, 2, 3], outputValues) + + disposable?.dispose() + } + + func test_publisher_isDisposedIfNotUsedInWorkflow() { + let expectation = XCTestExpectation(description: "SignalProducer should be disposed if no longer used.") + let publisher = [1, 2, 3] + .publisher + .handleEvents(receiveCompletion: { _ in + expectation.fulfill() + }) + .eraseToAnyPublisher() + + let host = WorkflowHost( + workflow: PublisherWorkflow(publisher: publisher) + ) + + let publisherTwo = [1, 2, 3].publisher.eraseToAnyPublisher() + host.update(workflow: PublisherWorkflow(publisher: publisherTwo)) + + wait(for: [expectation], timeout: 1) + } } diff --git a/WorkflowCombine/Tests/WorkerTests.swift b/WorkflowCombine/Tests/WorkerTests.swift index bae2720af..c3930b693 100644 --- a/WorkflowCombine/Tests/WorkerTests.swift +++ b/WorkflowCombine/Tests/WorkerTests.swift @@ -136,18 +136,14 @@ class WorkerTests: XCTestCase { } struct TestWorker: Worker { - private let publisher = PassthroughSubject() - func isEquivalent(to otherWorker: TestWorker) -> Bool { true } func run() -> AnyPublisher { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - publisher.send(1) - publisher.send(2) - } - return publisher.eraseToAnyPublisher() + [1, 2].publisher + .delay(for: .milliseconds(1), scheduler: RunLoop.main) + .eraseToAnyPublisher() } } From c647ee7074ccbfb2f7c8f1a9f8ef4d2cc27246f7 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Mon, 1 Nov 2021 09:17:44 -0600 Subject: [PATCH 06/17] Create a sample app for Combine backed worker --- Package.swift | 10 - Samples/WorkflowCombineSampleApp/Podfile | 9 + Samples/WorkflowCombineSampleApp/Podfile.lock | 58 +++ .../project.pbxproj | 408 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AppDelegate.swift | 30 ++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 ++ .../Base.lproj/Main.storyboard | 24 ++ .../DemoViewController.swift | 31 ++ .../WorkflowCombineSampleApp/DemoWorker.swift | 30 ++ .../DemoWorkflow.swift | 72 ++++ .../WorkflowCombineSampleApp/Info.plist | 66 +++ .../SceneDelegate.swift | 53 +++ .../ViewController.swift | 15 + WorkflowCombine.podspec | 2 +- 21 files changed, 970 insertions(+), 11 deletions(-) create mode 100644 Samples/WorkflowCombineSampleApp/Podfile create mode 100644 Samples/WorkflowCombineSampleApp/Podfile.lock create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/contents.xcworkspacedata create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/AppDelegate.swift create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/Contents.json create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/LaunchScreen.storyboard create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/Main.storyboard create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoViewController.swift create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Info.plist create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/SceneDelegate.swift create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/ViewController.swift diff --git a/Package.swift b/Package.swift index 73615c6b9..21dd18027 100644 --- a/Package.swift +++ b/Package.swift @@ -71,16 +71,6 @@ let package = Package( dependencies: ["Workflow"], path: "WorkflowCombine/Sources" ), - .testTarget( - name: "WorkflowCombineTests", - dependencies: ["Workflow", "WorkflowCombine", "WorkflowTesting"], - path: "WorkflowCombine/Tests" - ), - .target( - name: "WorkflowTesting", - dependencies: ["Workflow"], - path: "WorkflowTesting/Sources" - ), ], swiftLanguageVersions: [.v5] ) diff --git a/Samples/WorkflowCombineSampleApp/Podfile b/Samples/WorkflowCombineSampleApp/Podfile new file mode 100644 index 000000000..775b9423d --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/Podfile @@ -0,0 +1,9 @@ +project 'WorkflowCombineSampleApp.xcodeproj' +platform :ios, '13.0' + +target 'WorkflowCombineSampleApp' do + pod 'Workflow', path: '../../Workflow.podspec', :testspecs => ['Tests'] + pod 'WorkflowUI', path: '../../WorkflowUI.podspec', :testspecs => ['Tests'] + pod 'WorkflowCombine', path: '../../WorkflowCombine.podspec' + pod 'WorkflowReactiveSwift', path: '../../WorkflowReactiveSwift.podspec', :testspecs => ['Tests'] +end \ No newline at end of file diff --git a/Samples/WorkflowCombineSampleApp/Podfile.lock b/Samples/WorkflowCombineSampleApp/Podfile.lock new file mode 100644 index 000000000..2307817a0 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/Podfile.lock @@ -0,0 +1,58 @@ +PODS: + - ReactiveSwift (6.3.0) + - Workflow (1.0.0-rc.1): + - ReactiveSwift (~> 6.3.0) + - Workflow/Tests (1.0.0-rc.1): + - ReactiveSwift (~> 6.3.0) + - WorkflowCombine (1.0.0-rc.1): + - Workflow (= 1.0.0-rc.1) + - WorkflowReactiveSwift (1.0.0-rc.1): + - ReactiveSwift (~> 6.3.0) + - Workflow (= 1.0.0-rc.1) + - WorkflowReactiveSwift/Tests (1.0.0-rc.1): + - ReactiveSwift (~> 6.3.0) + - Workflow (= 1.0.0-rc.1) + - WorkflowTesting (= 1.0.0-rc.1) + - WorkflowTesting (1.0.0-rc.1): + - Workflow (= 1.0.0-rc.1) + - WorkflowUI (1.0.0-rc.1): + - Workflow (= 1.0.0-rc.1) + - WorkflowUI/Tests (1.0.0-rc.1): + - Workflow (= 1.0.0-rc.1) + - WorkflowReactiveSwift (= 1.0.0-rc.1) + +DEPENDENCIES: + - Workflow (from `../../Workflow.podspec`) + - Workflow/Tests (from `../../Workflow.podspec`) + - WorkflowCombine (from `../../WorkflowCombine.podspec`) + - WorkflowReactiveSwift (from `../../WorkflowReactiveSwift.podspec`) + - WorkflowReactiveSwift/Tests (from `../../WorkflowReactiveSwift.podspec`) + - WorkflowUI (from `../../WorkflowUI.podspec`) + - WorkflowUI/Tests (from `../../WorkflowUI.podspec`) + +SPEC REPOS: + trunk: + - ReactiveSwift + - WorkflowTesting + +EXTERNAL SOURCES: + Workflow: + :path: "../../Workflow.podspec" + WorkflowCombine: + :path: "../../WorkflowCombine.podspec" + WorkflowReactiveSwift: + :path: "../../WorkflowReactiveSwift.podspec" + WorkflowUI: + :path: "../../WorkflowUI.podspec" + +SPEC CHECKSUMS: + ReactiveSwift: 7937f26ec18d555e2a99352bd0a8b2817d1f042d + Workflow: e3391755e611eb0e0efc244ef5fa9b12c6e2f8ab + WorkflowCombine: 8806745d6023261bfbc34e0d1da753774e9fe0c5 + WorkflowReactiveSwift: a1854c330a7ea4342e999156e0abe7f216e16d63 + WorkflowTesting: ff584e427bda8491285deb97db14182925d1e4f4 + WorkflowUI: 0e3b93590dc2e58a93f300a47e5c8e81b501ef36 + +PODFILE CHECKSUM: 59f19f06532469f70596f7954b3f8fe590c38ef1 + +COCOAPODS: 1.9.1 diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj new file mode 100644 index 000000000..8ec1bb757 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj @@ -0,0 +1,408 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 9A8259C8C723B39466D03271 /* libPods-WorkflowCombineSampleApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB177673F3D3F7E40B2E3F52 /* libPods-WorkflowCombineSampleApp.a */; }; + F91FC5E1272B5EF000F19C3D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5E0272B5EF000F19C3D /* AppDelegate.swift */; }; + F91FC5E3272B5EF000F19C3D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5E2272B5EF000F19C3D /* SceneDelegate.swift */; }; + F91FC5E5272B5EF000F19C3D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5E4272B5EF000F19C3D /* ViewController.swift */; }; + F91FC5E8272B5EF000F19C3D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F91FC5E6272B5EF000F19C3D /* Main.storyboard */; }; + F91FC5EA272B5EF700F19C3D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F91FC5E9272B5EF700F19C3D /* Assets.xcassets */; }; + F91FC5ED272B5EF700F19C3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F91FC5EB272B5EF700F19C3D /* LaunchScreen.storyboard */; }; + F91FC5F6272B5F0500F19C3D /* DemoWorkflow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5F4272B5F0500F19C3D /* DemoWorkflow.swift */; }; + F91FC5F7272B5F0500F19C3D /* DemoWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5F5272B5F0500F19C3D /* DemoWorker.swift */; }; + F91FC5F9272B5FBB00F19C3D /* DemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5F8272B5FBB00F19C3D /* DemoViewController.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 9CD0CAA819EECAD20D89C730 /* Pods-WorkflowCombineSampleApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp.debug.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp/Pods-WorkflowCombineSampleApp.debug.xcconfig"; sourceTree = ""; }; + DCBB19D1BA1D3E7BDFA35AAC /* Pods-WorkflowCombineSampleApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp.release.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp/Pods-WorkflowCombineSampleApp.release.xcconfig"; sourceTree = ""; }; + F91FC5DD272B5EF000F19C3D /* WorkflowCombineSampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WorkflowCombineSampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F91FC5E0272B5EF000F19C3D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + F91FC5E2272B5EF000F19C3D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + F91FC5E4272B5EF000F19C3D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + F91FC5E7272B5EF000F19C3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + F91FC5E9272B5EF700F19C3D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + F91FC5EC272B5EF700F19C3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + F91FC5EE272B5EF700F19C3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F91FC5F4272B5F0500F19C3D /* DemoWorkflow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoWorkflow.swift; sourceTree = ""; }; + F91FC5F5272B5F0500F19C3D /* DemoWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoWorker.swift; sourceTree = ""; }; + F91FC5F8272B5FBB00F19C3D /* DemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoViewController.swift; sourceTree = ""; }; + FB177673F3D3F7E40B2E3F52 /* libPods-WorkflowCombineSampleApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WorkflowCombineSampleApp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F91FC5DA272B5EF000F19C3D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A8259C8C723B39466D03271 /* libPods-WorkflowCombineSampleApp.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2301A5C693F40633B788F033 /* Frameworks */ = { + isa = PBXGroup; + children = ( + FB177673F3D3F7E40B2E3F52 /* libPods-WorkflowCombineSampleApp.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5B949144518AC7D6FECAFCBF /* Pods */ = { + isa = PBXGroup; + children = ( + 9CD0CAA819EECAD20D89C730 /* Pods-WorkflowCombineSampleApp.debug.xcconfig */, + DCBB19D1BA1D3E7BDFA35AAC /* Pods-WorkflowCombineSampleApp.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + F91FC5D4272B5EF000F19C3D = { + isa = PBXGroup; + children = ( + F91FC5DF272B5EF000F19C3D /* WorkflowCombineSampleApp */, + F91FC5DE272B5EF000F19C3D /* Products */, + 5B949144518AC7D6FECAFCBF /* Pods */, + 2301A5C693F40633B788F033 /* Frameworks */, + ); + sourceTree = ""; + }; + F91FC5DE272B5EF000F19C3D /* Products */ = { + isa = PBXGroup; + children = ( + F91FC5DD272B5EF000F19C3D /* WorkflowCombineSampleApp.app */, + ); + name = Products; + sourceTree = ""; + }; + F91FC5DF272B5EF000F19C3D /* WorkflowCombineSampleApp */ = { + isa = PBXGroup; + children = ( + F91FC5F5272B5F0500F19C3D /* DemoWorker.swift */, + F91FC5F4272B5F0500F19C3D /* DemoWorkflow.swift */, + F91FC5E0272B5EF000F19C3D /* AppDelegate.swift */, + F91FC5E2272B5EF000F19C3D /* SceneDelegate.swift */, + F91FC5F8272B5FBB00F19C3D /* DemoViewController.swift */, + F91FC5E4272B5EF000F19C3D /* ViewController.swift */, + F91FC5E6272B5EF000F19C3D /* Main.storyboard */, + F91FC5E9272B5EF700F19C3D /* Assets.xcassets */, + F91FC5EB272B5EF700F19C3D /* LaunchScreen.storyboard */, + F91FC5EE272B5EF700F19C3D /* Info.plist */, + ); + path = WorkflowCombineSampleApp; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F91FC5DC272B5EF000F19C3D /* WorkflowCombineSampleApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = F91FC5F1272B5EF700F19C3D /* Build configuration list for PBXNativeTarget "WorkflowCombineSampleApp" */; + buildPhases = ( + BC8BD4A8482F12EE585D17CE /* [CP] Check Pods Manifest.lock */, + F91FC5D9272B5EF000F19C3D /* Sources */, + F91FC5DA272B5EF000F19C3D /* Frameworks */, + F91FC5DB272B5EF000F19C3D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WorkflowCombineSampleApp; + productName = WorkflowCombineSampleApp; + productReference = F91FC5DD272B5EF000F19C3D /* WorkflowCombineSampleApp.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F91FC5D5272B5EF000F19C3D /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1250; + TargetAttributes = { + F91FC5DC272B5EF000F19C3D = { + CreatedOnToolsVersion = 12.5.1; + }; + }; + }; + buildConfigurationList = F91FC5D8272B5EF000F19C3D /* Build configuration list for PBXProject "WorkflowCombineSampleApp" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = F91FC5D4272B5EF000F19C3D; + productRefGroup = F91FC5DE272B5EF000F19C3D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F91FC5DC272B5EF000F19C3D /* WorkflowCombineSampleApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + F91FC5DB272B5EF000F19C3D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F91FC5ED272B5EF700F19C3D /* LaunchScreen.storyboard in Resources */, + F91FC5EA272B5EF700F19C3D /* Assets.xcassets in Resources */, + F91FC5E8272B5EF000F19C3D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + BC8BD4A8482F12EE585D17CE /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-WorkflowCombineSampleApp-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F91FC5D9272B5EF000F19C3D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F91FC5E5272B5EF000F19C3D /* ViewController.swift in Sources */, + F91FC5F7272B5F0500F19C3D /* DemoWorker.swift in Sources */, + F91FC5F6272B5F0500F19C3D /* DemoWorkflow.swift in Sources */, + F91FC5F9272B5FBB00F19C3D /* DemoViewController.swift in Sources */, + F91FC5E1272B5EF000F19C3D /* AppDelegate.swift in Sources */, + F91FC5E3272B5EF000F19C3D /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + F91FC5E6272B5EF000F19C3D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F91FC5E7272B5EF000F19C3D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + F91FC5EB272B5EF700F19C3D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F91FC5EC272B5EF700F19C3D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + F91FC5EF272B5EF700F19C3D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + F91FC5F0272B5EF700F19C3D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + F91FC5F2272B5EF700F19C3D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9CD0CAA819EECAD20D89C730 /* Pods-WorkflowCombineSampleApp.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = WorkflowCombineSampleApp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.soorinpark.parkornapp.WorkflowCombineSampleApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F91FC5F3272B5EF700F19C3D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DCBB19D1BA1D3E7BDFA35AAC /* Pods-WorkflowCombineSampleApp.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = WorkflowCombineSampleApp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.soorinpark.parkornapp.WorkflowCombineSampleApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F91FC5D8272B5EF000F19C3D /* Build configuration list for PBXProject "WorkflowCombineSampleApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F91FC5EF272B5EF700F19C3D /* Debug */, + F91FC5F0272B5EF700F19C3D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F91FC5F1272B5EF700F19C3D /* Build configuration list for PBXNativeTarget "WorkflowCombineSampleApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F91FC5F2272B5EF700F19C3D /* Debug */, + F91FC5F3272B5EF700F19C3D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F91FC5D5272B5EF000F19C3D /* Project object */; +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/contents.xcworkspacedata b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..c8c598923 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/AppDelegate.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/AppDelegate.swift new file mode 100644 index 000000000..ec87c1c79 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/AppDelegate.swift @@ -0,0 +1,30 @@ +// +// AppDelegate.swift +// WorkflowCombineSampleApp +// +// Created by Soo Rin Park on 10/28/21. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AccentColor.colorset/Contents.json b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..9221b9bb1 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/Contents.json b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/LaunchScreen.storyboard b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..865e9329f --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/Main.storyboard b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/Main.storyboard new file mode 100644 index 000000000..25a763858 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoViewController.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoViewController.swift new file mode 100644 index 000000000..1927c7c5d --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoViewController.swift @@ -0,0 +1,31 @@ +// +// DemoViewController.swift +// WorkflowCombineSampleApp +// +// Created by Soo Rin Park on 10/28/21. +// + +import Foundation +import UIKit +import WorkflowUI + +class DemoViewController: ScreenViewController { + private let label = UILabel() + + override func viewDidLoad() { + super.viewDidLoad() + + label.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(label) + label.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + label.topAnchor.constraint(equalTo: view.topAnchor).isActive = true + label.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + label.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + } + + override func screenDidChange(from previousScreen: DemoScreen, previousEnvironment: ViewEnvironment) { + super.screenDidChange(from: previousScreen, previousEnvironment: previousEnvironment) + + label.text = screen.date + } +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift new file mode 100644 index 000000000..10f69fa06 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift @@ -0,0 +1,30 @@ +// +// DemoWorker.swift +// WorkflowCombineSampleApp +// +// Created by Soo Rin Park on 10/28/21. +// + +import Combine +import Workflow +import WorkflowCombine +import WorkflowUI + +// MARK: Workers + +extension DemoWorkflow { + struct DemoWorker: Worker { + typealias Output = Action + + // This publisher publishes the current date on a timer that fires every second + func run() -> AnyPublisher { + Timer.publish(every: 1, on: .main, in: .common) + .autoconnect() + .replaceError(with: Date()) + .map { .init(publishedDate: $0) } + .eraseToAnyPublisher() + } + + func isEquivalent(to otherWorker: DemoWorkflow.DemoWorker) -> Bool { true } + } +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift new file mode 100644 index 000000000..81dbd97e4 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift @@ -0,0 +1,72 @@ +// +// DemoWorkflow.swift +// WorkflowCombineSampleApp +// +// Created by Soo Rin Park on 10/28/21. +// + +import Combine +import Workflow +import WorkflowUI + +// MARK: Input and Output + +let dateFormatter = DateFormatter() + +struct DemoWorkflow: Workflow { + typealias Output = Never +} + +// MARK: State and Initialization + +extension DemoWorkflow { + struct State { + var date: Date + } + + func makeInitialState() -> DemoWorkflow.State { State(date: Date()) } + + func workflowDidChange(from previousWorkflow: DemoWorkflow, state: inout State) {} +} + +// MARK: Actions + +extension DemoWorkflow { + struct Action: WorkflowAction { + typealias WorkflowType = DemoWorkflow + + let publishedDate: Date + + func apply(toState state: inout DemoWorkflow.State) -> DemoWorkflow.Output? { + state.date = publishedDate + return nil + } + } +} + +// MARK: Rendering + +extension DemoWorkflow { + // TODO: Change this to your actual rendering type + typealias Rendering = DemoScreen + + func render(state: DemoWorkflow.State, context: RenderContext) -> Rendering { + DemoWorker() + .rendered(in: context) + + dateFormatter.dateStyle = .long + dateFormatter.timeStyle = .long + let formattedDate = dateFormatter.string(from: state.date) + let rendering = Rendering(date: formattedDate) + + return rendering + } +} + +struct DemoScreen: Screen { + let date: String + + func viewControllerDescription(environment: ViewEnvironment) -> ViewControllerDescription { + DemoViewController.description(for: self, environment: environment) + } +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Info.plist b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Info.plist new file mode 100644 index 000000000..5b531f7b2 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Info.plist @@ -0,0 +1,66 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/SceneDelegate.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/SceneDelegate.swift new file mode 100644 index 000000000..135e6cf16 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/SceneDelegate.swift @@ -0,0 +1,53 @@ +// +// SceneDelegate.swift +// WorkflowCombineSampleApp +// +// Created by Soo Rin Park on 10/28/21. +// + +import UIKit +import WorkflowUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let windowScene = (scene as? UIWindowScene) else { return } + let window = UIWindow(windowScene: windowScene) + let viewController = ContainerViewController(workflow: DemoWorkflow()) + window.rootViewController = viewController + self.window = window + window.makeKeyAndVisible() + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/ViewController.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/ViewController.swift new file mode 100644 index 000000000..f582053e8 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/ViewController.swift @@ -0,0 +1,15 @@ +// +// ViewController.swift +// WorkflowCombineSampleApp +// +// Created by Soo Rin Park on 10/28/21. +// + +import UIKit + +class ViewController: UIViewController { + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } +} diff --git a/WorkflowCombine.podspec b/WorkflowCombine.podspec index 7ee44c369..0c6e61232 100644 --- a/WorkflowCombine.podspec +++ b/WorkflowCombine.podspec @@ -17,7 +17,7 @@ Pod::Spec.new do |s| s.osx.deployment_target = '10.15' s.source_files = 'WorkflowCombine/Sources/*.swift' - + s.dependency 'Workflow', "#{s.version}" end From 9f0b8fcaad88356d32ec36eeeeed9be3e0beadd3 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Mon, 1 Nov 2021 09:16:59 -0600 Subject: [PATCH 07/17] Fixed testPublisherWorkflow's TestWorkflow implmentation --- WorkflowCombine/Tests/AnyPublisherTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WorkflowCombine/Tests/AnyPublisherTests.swift b/WorkflowCombine/Tests/AnyPublisherTests.swift index 0791193fe..ad63a0e36 100644 --- a/WorkflowCombine/Tests/AnyPublisherTests.swift +++ b/WorkflowCombine/Tests/AnyPublisherTests.swift @@ -33,9 +33,9 @@ class AnyPublisherTests: XCTestCase { typealias Rendering = Void func render(state: State, context: RenderContext) -> Rendering { - Just(1) - .map { _ in AnyWorkflowAction.noAction } + [1].publisher .eraseToAnyPublisher() + .mapOutput { _ in AnyWorkflowAction.noAction } .running(in: context, key: "123") } } From c00fecc88cad6d159de69a6f8ba710c5cdd12d5c Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Mon, 1 Nov 2021 09:24:35 -0600 Subject: [PATCH 08/17] Remove WorkflowCombineTestingTests target from podspec --- Development.podspec | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/Development.podspec b/Development.podspec index 34e3cd29d..c4ab219a6 100644 --- a/Development.podspec +++ b/Development.podspec @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.dependency 'WorkflowUI' s.dependency 'WorkflowReactiveSwift' s.dependency 'WorkflowRxSwift' - # s.dependency 'WorkflowCombine' # TODO: Disabled because app specs cannot increase the deployment target of the root +# s.dependency 'WorkflowCombine' # TODO: Disabled because app specs cannot increase the deployment target of the root s.source_files = 'Samples/Dummy.swift' s.subspec 'Dummy' do |ss| @@ -143,21 +143,13 @@ Pod::Spec.new do |s| test_spec.dependency 'WorkflowRxSwiftTesting' end - # TODO: Disabled because app specs cannot increase the deployment target of the root - # To use, increase the deployment target of this spec to 13.0 or higher - # s.test_spec 'WorkflowCombineTests' do |test_spec| - # test_spec.requires_app_host = true - # test_spec.source_files = 'WorkflowCombine/Tests/**/*.swift' - # test_spec.framework = 'XCTest' - # test_spec.dependency 'WorkflowTesting' - # test_spec.dependency 'WorkflowCombineTesting' - # end - - # s.test_spec 'WorkflowCombineTestingTests' do |test_spec| - # test_spec.requires_app_host = true - # test_spec.source_files = 'WorkflowCombine/TestingTests/**/*.swift' - # test_spec.framework = 'XCTest' - # test_spec.dependency 'WorkflowTesting' - # test_spec.dependency 'WorkflowCombineTesting' - # end +# TODO: Disabled because app specs cannot increase the deployment target of the root +# To use, increase the deployment target of this spec to 13.0 or higher +# s.test_spec 'WorkflowCombineTests' do |test_spec| +# test_spec.requires_app_host = true +# test_spec.source_files = 'WorkflowCombine/Tests/**/*.swift' +# test_spec.framework = 'XCTest' +# test_spec.dependency 'WorkflowTesting' +# test_spec.dependency 'WorkflowCombineTesting' +# end end From f7a096b46eb4b060b841ef81202df964c2e008f6 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Mon, 1 Nov 2021 10:42:50 -0600 Subject: [PATCH 09/17] Add tests to WorkflowCombineSampleApp --- Samples/WorkflowCombineSampleApp/Podfile | 9 + Samples/WorkflowCombineSampleApp/Podfile.lock | 2 +- Samples/WorkflowCombineSampleApp/README.md | 10 ++ .../project.pbxproj | 162 ++++++++++++++++++ .../WorkflowCombineSampleApp/DemoWorker.swift | 21 ++- .../DemoWorkflow.swift | 1 - .../Info.plist | 22 +++ .../WorkflowCombineSampleAppUnitTests.swift | 28 +++ 8 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 Samples/WorkflowCombineSampleApp/README.md create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/Info.plist create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift diff --git a/Samples/WorkflowCombineSampleApp/Podfile b/Samples/WorkflowCombineSampleApp/Podfile index 775b9423d..92ae42eab 100644 --- a/Samples/WorkflowCombineSampleApp/Podfile +++ b/Samples/WorkflowCombineSampleApp/Podfile @@ -6,4 +6,13 @@ target 'WorkflowCombineSampleApp' do pod 'WorkflowUI', path: '../../WorkflowUI.podspec', :testspecs => ['Tests'] pod 'WorkflowCombine', path: '../../WorkflowCombine.podspec' pod 'WorkflowReactiveSwift', path: '../../WorkflowReactiveSwift.podspec', :testspecs => ['Tests'] + + target 'WorkflowCombineSampleAppUnitTests' do + inherit! :search_paths + + pod 'Workflow', path: '../../Workflow.podspec', :testspecs => ['Tests'] + pod 'WorkflowCombine', path: '../../WorkflowCombine.podspec' + pod 'WorkflowReactiveSwift', path: '../../WorkflowReactiveSwift.podspec', :testspecs => ['Tests'] + pod 'WorkflowUI', path: '../../WorkflowUI.podspec', :testspecs => ['Tests'] + end end \ No newline at end of file diff --git a/Samples/WorkflowCombineSampleApp/Podfile.lock b/Samples/WorkflowCombineSampleApp/Podfile.lock index 2307817a0..3dd582e02 100644 --- a/Samples/WorkflowCombineSampleApp/Podfile.lock +++ b/Samples/WorkflowCombineSampleApp/Podfile.lock @@ -53,6 +53,6 @@ SPEC CHECKSUMS: WorkflowTesting: ff584e427bda8491285deb97db14182925d1e4f4 WorkflowUI: 0e3b93590dc2e58a93f300a47e5c8e81b501ef36 -PODFILE CHECKSUM: 59f19f06532469f70596f7954b3f8fe590c38ef1 +PODFILE CHECKSUM: 0a8cc7b6b118ae7091c5f1104b8d8cbffe43c04f COCOAPODS: 1.9.1 diff --git a/Samples/WorkflowCombineSampleApp/README.md b/Samples/WorkflowCombineSampleApp/README.md new file mode 100644 index 000000000..7d24ea00b --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/README.md @@ -0,0 +1,10 @@ +This sample project utilizes the WorkflowCombine library to demonstrate its usage in a `Workflow`. It is a simple app with a label that updates the current date & time every second. + +# Usage + +The api design is identical to that of the current `WorkflowReactiveSwift` library, so to migrate your `Worker`s to use the `WorkflowCombine` library, all you should have to do is to switch the library import statement, as well + + +### Notes + +This library does **not** remove the usage of the `ReactiveSwift` library from the `Workflow` library. Currently the `Workflow` implementation is tightly coupled with `ReactiveSwift`, and this library is only limited to the `Worker`. Therefore, when utilizing both the existing `Workflow` and the new `Combine` backed `Worker` , you will need to utilize both the `ReactiveSwift` and `Combine` libraries. \ No newline at end of file diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj index 8ec1bb757..c063090c0 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj @@ -7,7 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 765F2E2D94C8090C834913C7 /* libPods-WorkflowCombineSampleAppUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B3BB606C2D22FA463DF9FE9 /* libPods-WorkflowCombineSampleAppUnitTests.a */; }; 9A8259C8C723B39466D03271 /* libPods-WorkflowCombineSampleApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB177673F3D3F7E40B2E3F52 /* libPods-WorkflowCombineSampleApp.a */; }; + F9148E582730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9148E572730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift */; }; F91FC5E1272B5EF000F19C3D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5E0272B5EF000F19C3D /* AppDelegate.swift */; }; F91FC5E3272B5EF000F19C3D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5E2272B5EF000F19C3D /* SceneDelegate.swift */; }; F91FC5E5272B5EF000F19C3D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5E4272B5EF000F19C3D /* ViewController.swift */; }; @@ -19,9 +21,27 @@ F91FC5F9272B5FBB00F19C3D /* DemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5F8272B5FBB00F19C3D /* DemoViewController.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + F9148E5A2730496400A2A822 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F91FC5D5272B5EF000F19C3D /* Project object */; + proxyType = 1; + remoteGlobalIDString = F91FC5DC272B5EF000F19C3D; + remoteInfo = WorkflowCombineSampleApp; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ + 5B3BB606C2D22FA463DF9FE9 /* libPods-WorkflowCombineSampleAppUnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WorkflowCombineSampleAppUnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6EC8006ECC2D615830927194 /* Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleAppUnitTests/Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig"; sourceTree = ""; }; 9CD0CAA819EECAD20D89C730 /* Pods-WorkflowCombineSampleApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp.debug.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp/Pods-WorkflowCombineSampleApp.debug.xcconfig"; sourceTree = ""; }; + CD5C74CDD3F93E878B7A7ED3 /* Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.debug.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests/Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.debug.xcconfig"; sourceTree = ""; }; + D83DC771AA69FE4772B1ADD3 /* Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.release.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests/Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.release.xcconfig"; sourceTree = ""; }; DCBB19D1BA1D3E7BDFA35AAC /* Pods-WorkflowCombineSampleApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp.release.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp/Pods-WorkflowCombineSampleApp.release.xcconfig"; sourceTree = ""; }; + DE091C17D6BFBA3117FD439A /* Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleAppUnitTests/Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig"; sourceTree = ""; }; + F9148E552730496400A2A822 /* WorkflowCombineSampleAppUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WorkflowCombineSampleAppUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F9148E572730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkflowCombineSampleAppUnitTests.swift; sourceTree = ""; }; + F9148E592730496400A2A822 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F91FC5DD272B5EF000F19C3D /* WorkflowCombineSampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WorkflowCombineSampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; F91FC5E0272B5EF000F19C3D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; F91FC5E2272B5EF000F19C3D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -37,6 +57,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + F9148E522730496400A2A822 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 765F2E2D94C8090C834913C7 /* libPods-WorkflowCombineSampleAppUnitTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F91FC5DA272B5EF000F19C3D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -52,6 +80,7 @@ isa = PBXGroup; children = ( FB177673F3D3F7E40B2E3F52 /* libPods-WorkflowCombineSampleApp.a */, + 5B3BB606C2D22FA463DF9FE9 /* libPods-WorkflowCombineSampleAppUnitTests.a */, ); name = Frameworks; sourceTree = ""; @@ -61,14 +90,28 @@ children = ( 9CD0CAA819EECAD20D89C730 /* Pods-WorkflowCombineSampleApp.debug.xcconfig */, DCBB19D1BA1D3E7BDFA35AAC /* Pods-WorkflowCombineSampleApp.release.xcconfig */, + DE091C17D6BFBA3117FD439A /* Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig */, + 6EC8006ECC2D615830927194 /* Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig */, + CD5C74CDD3F93E878B7A7ED3 /* Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.debug.xcconfig */, + D83DC771AA69FE4772B1ADD3 /* Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.release.xcconfig */, ); path = Pods; sourceTree = ""; }; + F9148E562730496400A2A822 /* WorkflowCombineSampleAppUnitTests */ = { + isa = PBXGroup; + children = ( + F9148E572730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift */, + F9148E592730496400A2A822 /* Info.plist */, + ); + path = WorkflowCombineSampleAppUnitTests; + sourceTree = ""; + }; F91FC5D4272B5EF000F19C3D = { isa = PBXGroup; children = ( F91FC5DF272B5EF000F19C3D /* WorkflowCombineSampleApp */, + F9148E562730496400A2A822 /* WorkflowCombineSampleAppUnitTests */, F91FC5DE272B5EF000F19C3D /* Products */, 5B949144518AC7D6FECAFCBF /* Pods */, 2301A5C693F40633B788F033 /* Frameworks */, @@ -79,6 +122,7 @@ isa = PBXGroup; children = ( F91FC5DD272B5EF000F19C3D /* WorkflowCombineSampleApp.app */, + F9148E552730496400A2A822 /* WorkflowCombineSampleAppUnitTests.xctest */, ); name = Products; sourceTree = ""; @@ -103,6 +147,25 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + F9148E542730496400A2A822 /* WorkflowCombineSampleAppUnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9148E5E2730496400A2A822 /* Build configuration list for PBXNativeTarget "WorkflowCombineSampleAppUnitTests" */; + buildPhases = ( + 910F3B6833A2504DA2EEA54D /* [CP] Check Pods Manifest.lock */, + F9148E512730496400A2A822 /* Sources */, + F9148E522730496400A2A822 /* Frameworks */, + F9148E532730496400A2A822 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F9148E5B2730496400A2A822 /* PBXTargetDependency */, + ); + name = WorkflowCombineSampleAppUnitTests; + productName = WorkflowCombineSampleAppUnitTests; + productReference = F9148E552730496400A2A822 /* WorkflowCombineSampleAppUnitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; F91FC5DC272B5EF000F19C3D /* WorkflowCombineSampleApp */ = { isa = PBXNativeTarget; buildConfigurationList = F91FC5F1272B5EF700F19C3D /* Build configuration list for PBXNativeTarget "WorkflowCombineSampleApp" */; @@ -130,6 +193,10 @@ LastSwiftUpdateCheck = 1250; LastUpgradeCheck = 1250; TargetAttributes = { + F9148E542730496400A2A822 = { + CreatedOnToolsVersion = 12.5.1; + TestTargetID = F91FC5DC272B5EF000F19C3D; + }; F91FC5DC272B5EF000F19C3D = { CreatedOnToolsVersion = 12.5.1; }; @@ -149,11 +216,19 @@ projectRoot = ""; targets = ( F91FC5DC272B5EF000F19C3D /* WorkflowCombineSampleApp */, + F9148E542730496400A2A822 /* WorkflowCombineSampleAppUnitTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + F9148E532730496400A2A822 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F91FC5DB272B5EF000F19C3D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -167,6 +242,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 910F3B6833A2504DA2EEA54D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-WorkflowCombineSampleAppUnitTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; BC8BD4A8482F12EE585D17CE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -192,6 +289,14 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + F9148E512730496400A2A822 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9148E582730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F91FC5D9272B5EF000F19C3D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -207,6 +312,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + F9148E5B2730496400A2A822 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F91FC5DC272B5EF000F19C3D /* WorkflowCombineSampleApp */; + targetProxy = F9148E5A2730496400A2A822 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ F91FC5E6272B5EF000F19C3D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -227,6 +340,46 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + F9148E5C2730496400A2A822 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DE091C17D6BFBA3117FD439A /* Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = WorkflowCombineSampleAppUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.soorinpark.parkornapp.WorkflowCombineSampleAppUnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WorkflowCombineSampleApp.app/WorkflowCombineSampleApp"; + }; + name = Debug; + }; + F9148E5D2730496400A2A822 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6EC8006ECC2D615830927194 /* Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = WorkflowCombineSampleAppUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.soorinpark.parkornapp.WorkflowCombineSampleAppUnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WorkflowCombineSampleApp.app/WorkflowCombineSampleApp"; + }; + name = Release; + }; F91FC5EF272B5EF700F19C3D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -384,6 +537,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + F9148E5E2730496400A2A822 /* Build configuration list for PBXNativeTarget "WorkflowCombineSampleAppUnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9148E5C2730496400A2A822 /* Debug */, + F9148E5D2730496400A2A822 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F91FC5D8272B5EF000F19C3D /* Build configuration list for PBXProject "WorkflowCombineSampleApp" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift index 10f69fa06..2b7dc0cf9 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift @@ -6,21 +6,22 @@ // import Combine +import ReactiveSwift import Workflow import WorkflowCombine +import WorkflowReactiveSwift import WorkflowUI // MARK: Workers extension DemoWorkflow { - struct DemoWorker: Worker { + struct DemoWorker: WorkflowCombine.Worker { typealias Output = Action // This publisher publishes the current date on a timer that fires every second func run() -> AnyPublisher { Timer.publish(every: 1, on: .main, in: .common) .autoconnect() - .replaceError(with: Date()) .map { .init(publishedDate: $0) } .eraseToAnyPublisher() } @@ -28,3 +29,19 @@ extension DemoWorkflow { func isEquivalent(to otherWorker: DemoWorkflow.DemoWorker) -> Bool { true } } } + +/// Identifcal implementation of the Combine Worker using the WorkflowReactiveSwift library instead. +/// To ensure that both implementations are correct, run the test suite with each implementation uncommented. +// extension DemoWorkflow { +// struct DemoWorker: WorkflowReactiveSwift.Worker { +// typealias Output = Action +// +// func run() -> SignalProducer { +// SignalProducer +// .timer(interval: DispatchTimeInterval.seconds(1), on: QueueScheduler()) +// .map { .init(publishedDate: $0) } +// } +// +// func isEquivalent(to otherWorker: DemoWorkflow.DemoWorker) -> Bool { true } +// } +// } diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift index 81dbd97e4..bc1e286ac 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift @@ -5,7 +5,6 @@ // Created by Soo Rin Park on 10/28/21. // -import Combine import Workflow import WorkflowUI diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/Info.plist b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/Info.plist new file mode 100644 index 000000000..64d65ca49 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift new file mode 100644 index 000000000..5de82deab --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift @@ -0,0 +1,28 @@ +// +// WorkflowCombineSampleAppUnitTests.swift +// WorkflowCombineSampleAppUnitTests +// +// Created by Soo Rin Park on 11/1/21. +// + +import Combine +import Workflow +import XCTest +@testable import WorkflowCombineSampleApp + +class DemoWorkerTests: XCTestCase { + func test_workflowIsRenderedEverySecondForFiveSeconds() { + let host = WorkflowHost(workflow: DemoWorkflow()) + + let expectation = XCTestExpectation(description: "host rendering is updated every second") + expectation.expectedFulfillmentCount = 5 + let disposable = host.rendering.signal.observeValues { rendering in + print(rendering) + expectation.fulfill() + } + + // buffer milisecond is added to account for the workflow to start running + wait(for: [expectation], timeout: 5.1) + disposable?.dispose() + } +} From ecf518de53adae9c11c1dce5450d7072d6426d94 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Mon, 1 Nov 2021 10:50:51 -0600 Subject: [PATCH 10/17] Add README for WorkflowCombineSampleApp --- Samples/WorkflowCombineSampleApp/README.md | 42 ++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/Samples/WorkflowCombineSampleApp/README.md b/Samples/WorkflowCombineSampleApp/README.md index 7d24ea00b..09bcaacd9 100644 --- a/Samples/WorkflowCombineSampleApp/README.md +++ b/Samples/WorkflowCombineSampleApp/README.md @@ -1,10 +1,48 @@ +# WorkflowCombineSampleApp + This sample project utilizes the WorkflowCombine library to demonstrate its usage in a `Workflow`. It is a simple app with a label that updates the current date & time every second. # Usage -The api design is identical to that of the current `WorkflowReactiveSwift` library, so to migrate your `Worker`s to use the `WorkflowCombine` library, all you should have to do is to switch the library import statement, as well +Thanks to the `AnyWorkflowConvertible` protocol, both the `WorkflowReactiveSwift` and `WorkflowCombine` Workers have identical api interface. To to migrate your `WorkflowReactiveSwift` `Worker`s to use the `WorkflowCombine` `Worker`s, you will need to do the following: + +1. Change `import` statements to use `WorkflowCombine` and `Combine` +2. Replace `SignalProducer` into `AnyPublisher` for the return type of `run` +3. Change the `run` implementation to the `Combine` equivalence. Most likely this will be the step that that requires the most attention, however if the tests were written for the `Workflow` it can be used to validate the new implementation without the need to change the tests. + +Below is an example of a simple `Worker` in both `WorkflowReactiveSwift` and `WorkflowCombine` that emits a signal every second with the current date. + +### `WorkflowReactiveSwift` + +```swift +struct TimerWorker: Worker { + typealias Output = Date + + func run() -> SignalProducer { + SignalProducer + .timer(interval: DispatchTimeInterval.seconds(1), on: QueueScheduler.main) + } + + func isEquivalent(to otherWorker: TimerWorker2) -> Bool { true } +} +``` + +### `WorkflowCombine` +```swift +struct TimerWorker: Worker { + typealias Output = Date + + func run() -> AnyPublisher { + Timer.publish(every: 1, on: .main, in: .common) + .autoconnect() + .eraseToAnyPublisher() + } + + func isEquivalent(to otherWorker: Self) -> Bool { true } +} +``` ### Notes -This library does **not** remove the usage of the `ReactiveSwift` library from the `Workflow` library. Currently the `Workflow` implementation is tightly coupled with `ReactiveSwift`, and this library is only limited to the `Worker`. Therefore, when utilizing both the existing `Workflow` and the new `Combine` backed `Worker` , you will need to utilize both the `ReactiveSwift` and `Combine` libraries. \ No newline at end of file +This library does **not** remove the usage of the `ReactiveSwift` library from the `Workflow` library. Currently the `Workflow` implementation is tightly coupled with `ReactiveSwift`, and this library is only limited to the `Worker`. Therefore, when utilizing both the existing `Workflow` and the new `Combine` backed `Worker` , you will need to utilize both the `ReactiveSwift` and `Combine` libraries. From 525a93653233dd37f526a26063d2d8229b688329 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Mon, 1 Nov 2021 12:57:45 -0600 Subject: [PATCH 11/17] Create WorkflowCombineSampleApp through podspec instead --- Development.podspec | 31 +- Samples/WorkflowCombineSampleApp/Podfile | 18 - Samples/WorkflowCombineSampleApp/Podfile.lock | 58 -- .../project.pbxproj | 570 ------------------ .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../contents.xcworkspacedata | 10 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../AppDelegate.swift | 24 +- .../SceneDelegate.swift | 53 -- .../WorkflowCombineSampleAppUnitTests.swift | 2 +- 11 files changed, 30 insertions(+), 759 deletions(-) delete mode 100644 Samples/WorkflowCombineSampleApp/Podfile delete mode 100644 Samples/WorkflowCombineSampleApp/Podfile.lock delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/contents.xcworkspacedata delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/SceneDelegate.swift diff --git a/Development.podspec b/Development.podspec index c4ab219a6..decdcafc6 100644 --- a/Development.podspec +++ b/Development.podspec @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.dependency 'WorkflowUI' s.dependency 'WorkflowReactiveSwift' s.dependency 'WorkflowRxSwift' -# s.dependency 'WorkflowCombine' # TODO: Disabled because app specs cannot increase the deployment target of the root + # s.dependency 'WorkflowCombine' # TODO: Disabled because app specs cannot increase the deployment target of the root s.source_files = 'Samples/Dummy.swift' s.subspec 'Dummy' do |ss| @@ -143,13 +143,24 @@ Pod::Spec.new do |s| test_spec.dependency 'WorkflowRxSwiftTesting' end -# TODO: Disabled because app specs cannot increase the deployment target of the root -# To use, increase the deployment target of this spec to 13.0 or higher -# s.test_spec 'WorkflowCombineTests' do |test_spec| -# test_spec.requires_app_host = true -# test_spec.source_files = 'WorkflowCombine/Tests/**/*.swift' -# test_spec.framework = 'XCTest' -# test_spec.dependency 'WorkflowTesting' -# test_spec.dependency 'WorkflowCombineTesting' -# end + # TODO: Disabled because app specs cannot increase the deployment target of the root + # To use, increase the deployment target of this spec to 13.0 or higher + # s.app_spec 'WorkflowCombineSampleApp' do |app_spec| + # app_spec.source_files = 'Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/**/*.swift' + # end + + # s.test_spec 'WorkflowCombineSampleAppTests' do |test_spec| + # test_spec.dependency 'Development/WorkflowCombineSampleApp' + # test_spec.requires_app_host = true + # test_spec.app_host_name = 'Development/WorkflowCombineSampleApp' + # test_spec.source_files = 'Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/**/*.swift' + # end +# + # s.test_spec 'WorkflowCombineTests' do |test_spec| + # test_spec.requires_app_host = true + # test_spec.source_files = 'WorkflowCombine/Tests/**/*.swift' + # test_spec.framework = 'XCTest' + # test_spec.dependency 'WorkflowTesting' + # test_spec.dependency 'WorkflowCombineTesting' + # end end diff --git a/Samples/WorkflowCombineSampleApp/Podfile b/Samples/WorkflowCombineSampleApp/Podfile deleted file mode 100644 index 92ae42eab..000000000 --- a/Samples/WorkflowCombineSampleApp/Podfile +++ /dev/null @@ -1,18 +0,0 @@ -project 'WorkflowCombineSampleApp.xcodeproj' -platform :ios, '13.0' - -target 'WorkflowCombineSampleApp' do - pod 'Workflow', path: '../../Workflow.podspec', :testspecs => ['Tests'] - pod 'WorkflowUI', path: '../../WorkflowUI.podspec', :testspecs => ['Tests'] - pod 'WorkflowCombine', path: '../../WorkflowCombine.podspec' - pod 'WorkflowReactiveSwift', path: '../../WorkflowReactiveSwift.podspec', :testspecs => ['Tests'] - - target 'WorkflowCombineSampleAppUnitTests' do - inherit! :search_paths - - pod 'Workflow', path: '../../Workflow.podspec', :testspecs => ['Tests'] - pod 'WorkflowCombine', path: '../../WorkflowCombine.podspec' - pod 'WorkflowReactiveSwift', path: '../../WorkflowReactiveSwift.podspec', :testspecs => ['Tests'] - pod 'WorkflowUI', path: '../../WorkflowUI.podspec', :testspecs => ['Tests'] - end -end \ No newline at end of file diff --git a/Samples/WorkflowCombineSampleApp/Podfile.lock b/Samples/WorkflowCombineSampleApp/Podfile.lock deleted file mode 100644 index 3dd582e02..000000000 --- a/Samples/WorkflowCombineSampleApp/Podfile.lock +++ /dev/null @@ -1,58 +0,0 @@ -PODS: - - ReactiveSwift (6.3.0) - - Workflow (1.0.0-rc.1): - - ReactiveSwift (~> 6.3.0) - - Workflow/Tests (1.0.0-rc.1): - - ReactiveSwift (~> 6.3.0) - - WorkflowCombine (1.0.0-rc.1): - - Workflow (= 1.0.0-rc.1) - - WorkflowReactiveSwift (1.0.0-rc.1): - - ReactiveSwift (~> 6.3.0) - - Workflow (= 1.0.0-rc.1) - - WorkflowReactiveSwift/Tests (1.0.0-rc.1): - - ReactiveSwift (~> 6.3.0) - - Workflow (= 1.0.0-rc.1) - - WorkflowTesting (= 1.0.0-rc.1) - - WorkflowTesting (1.0.0-rc.1): - - Workflow (= 1.0.0-rc.1) - - WorkflowUI (1.0.0-rc.1): - - Workflow (= 1.0.0-rc.1) - - WorkflowUI/Tests (1.0.0-rc.1): - - Workflow (= 1.0.0-rc.1) - - WorkflowReactiveSwift (= 1.0.0-rc.1) - -DEPENDENCIES: - - Workflow (from `../../Workflow.podspec`) - - Workflow/Tests (from `../../Workflow.podspec`) - - WorkflowCombine (from `../../WorkflowCombine.podspec`) - - WorkflowReactiveSwift (from `../../WorkflowReactiveSwift.podspec`) - - WorkflowReactiveSwift/Tests (from `../../WorkflowReactiveSwift.podspec`) - - WorkflowUI (from `../../WorkflowUI.podspec`) - - WorkflowUI/Tests (from `../../WorkflowUI.podspec`) - -SPEC REPOS: - trunk: - - ReactiveSwift - - WorkflowTesting - -EXTERNAL SOURCES: - Workflow: - :path: "../../Workflow.podspec" - WorkflowCombine: - :path: "../../WorkflowCombine.podspec" - WorkflowReactiveSwift: - :path: "../../WorkflowReactiveSwift.podspec" - WorkflowUI: - :path: "../../WorkflowUI.podspec" - -SPEC CHECKSUMS: - ReactiveSwift: 7937f26ec18d555e2a99352bd0a8b2817d1f042d - Workflow: e3391755e611eb0e0efc244ef5fa9b12c6e2f8ab - WorkflowCombine: 8806745d6023261bfbc34e0d1da753774e9fe0c5 - WorkflowReactiveSwift: a1854c330a7ea4342e999156e0abe7f216e16d63 - WorkflowTesting: ff584e427bda8491285deb97db14182925d1e4f4 - WorkflowUI: 0e3b93590dc2e58a93f300a47e5c8e81b501ef36 - -PODFILE CHECKSUM: 0a8cc7b6b118ae7091c5f1104b8d8cbffe43c04f - -COCOAPODS: 1.9.1 diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj deleted file mode 100644 index c063090c0..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.pbxproj +++ /dev/null @@ -1,570 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 765F2E2D94C8090C834913C7 /* libPods-WorkflowCombineSampleAppUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B3BB606C2D22FA463DF9FE9 /* libPods-WorkflowCombineSampleAppUnitTests.a */; }; - 9A8259C8C723B39466D03271 /* libPods-WorkflowCombineSampleApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB177673F3D3F7E40B2E3F52 /* libPods-WorkflowCombineSampleApp.a */; }; - F9148E582730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9148E572730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift */; }; - F91FC5E1272B5EF000F19C3D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5E0272B5EF000F19C3D /* AppDelegate.swift */; }; - F91FC5E3272B5EF000F19C3D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5E2272B5EF000F19C3D /* SceneDelegate.swift */; }; - F91FC5E5272B5EF000F19C3D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5E4272B5EF000F19C3D /* ViewController.swift */; }; - F91FC5E8272B5EF000F19C3D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F91FC5E6272B5EF000F19C3D /* Main.storyboard */; }; - F91FC5EA272B5EF700F19C3D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F91FC5E9272B5EF700F19C3D /* Assets.xcassets */; }; - F91FC5ED272B5EF700F19C3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F91FC5EB272B5EF700F19C3D /* LaunchScreen.storyboard */; }; - F91FC5F6272B5F0500F19C3D /* DemoWorkflow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5F4272B5F0500F19C3D /* DemoWorkflow.swift */; }; - F91FC5F7272B5F0500F19C3D /* DemoWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5F5272B5F0500F19C3D /* DemoWorker.swift */; }; - F91FC5F9272B5FBB00F19C3D /* DemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91FC5F8272B5FBB00F19C3D /* DemoViewController.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - F9148E5A2730496400A2A822 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F91FC5D5272B5EF000F19C3D /* Project object */; - proxyType = 1; - remoteGlobalIDString = F91FC5DC272B5EF000F19C3D; - remoteInfo = WorkflowCombineSampleApp; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 5B3BB606C2D22FA463DF9FE9 /* libPods-WorkflowCombineSampleAppUnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WorkflowCombineSampleAppUnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 6EC8006ECC2D615830927194 /* Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleAppUnitTests/Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig"; sourceTree = ""; }; - 9CD0CAA819EECAD20D89C730 /* Pods-WorkflowCombineSampleApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp.debug.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp/Pods-WorkflowCombineSampleApp.debug.xcconfig"; sourceTree = ""; }; - CD5C74CDD3F93E878B7A7ED3 /* Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.debug.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests/Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.debug.xcconfig"; sourceTree = ""; }; - D83DC771AA69FE4772B1ADD3 /* Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.release.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests/Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.release.xcconfig"; sourceTree = ""; }; - DCBB19D1BA1D3E7BDFA35AAC /* Pods-WorkflowCombineSampleApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleApp.release.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleApp/Pods-WorkflowCombineSampleApp.release.xcconfig"; sourceTree = ""; }; - DE091C17D6BFBA3117FD439A /* Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig"; path = "Target Support Files/Pods-WorkflowCombineSampleAppUnitTests/Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig"; sourceTree = ""; }; - F9148E552730496400A2A822 /* WorkflowCombineSampleAppUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WorkflowCombineSampleAppUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F9148E572730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkflowCombineSampleAppUnitTests.swift; sourceTree = ""; }; - F9148E592730496400A2A822 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F91FC5DD272B5EF000F19C3D /* WorkflowCombineSampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WorkflowCombineSampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; - F91FC5E0272B5EF000F19C3D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - F91FC5E2272B5EF000F19C3D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - F91FC5E4272B5EF000F19C3D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - F91FC5E7272B5EF000F19C3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - F91FC5E9272B5EF700F19C3D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - F91FC5EC272B5EF700F19C3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - F91FC5EE272B5EF700F19C3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F91FC5F4272B5F0500F19C3D /* DemoWorkflow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoWorkflow.swift; sourceTree = ""; }; - F91FC5F5272B5F0500F19C3D /* DemoWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoWorker.swift; sourceTree = ""; }; - F91FC5F8272B5FBB00F19C3D /* DemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoViewController.swift; sourceTree = ""; }; - FB177673F3D3F7E40B2E3F52 /* libPods-WorkflowCombineSampleApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WorkflowCombineSampleApp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - F9148E522730496400A2A822 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 765F2E2D94C8090C834913C7 /* libPods-WorkflowCombineSampleAppUnitTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F91FC5DA272B5EF000F19C3D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 9A8259C8C723B39466D03271 /* libPods-WorkflowCombineSampleApp.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 2301A5C693F40633B788F033 /* Frameworks */ = { - isa = PBXGroup; - children = ( - FB177673F3D3F7E40B2E3F52 /* libPods-WorkflowCombineSampleApp.a */, - 5B3BB606C2D22FA463DF9FE9 /* libPods-WorkflowCombineSampleAppUnitTests.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - 5B949144518AC7D6FECAFCBF /* Pods */ = { - isa = PBXGroup; - children = ( - 9CD0CAA819EECAD20D89C730 /* Pods-WorkflowCombineSampleApp.debug.xcconfig */, - DCBB19D1BA1D3E7BDFA35AAC /* Pods-WorkflowCombineSampleApp.release.xcconfig */, - DE091C17D6BFBA3117FD439A /* Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig */, - 6EC8006ECC2D615830927194 /* Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig */, - CD5C74CDD3F93E878B7A7ED3 /* Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.debug.xcconfig */, - D83DC771AA69FE4772B1ADD3 /* Pods-WorkflowCombineSampleApp-WorkflowCombineSampleAppUnitTests.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - F9148E562730496400A2A822 /* WorkflowCombineSampleAppUnitTests */ = { - isa = PBXGroup; - children = ( - F9148E572730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift */, - F9148E592730496400A2A822 /* Info.plist */, - ); - path = WorkflowCombineSampleAppUnitTests; - sourceTree = ""; - }; - F91FC5D4272B5EF000F19C3D = { - isa = PBXGroup; - children = ( - F91FC5DF272B5EF000F19C3D /* WorkflowCombineSampleApp */, - F9148E562730496400A2A822 /* WorkflowCombineSampleAppUnitTests */, - F91FC5DE272B5EF000F19C3D /* Products */, - 5B949144518AC7D6FECAFCBF /* Pods */, - 2301A5C693F40633B788F033 /* Frameworks */, - ); - sourceTree = ""; - }; - F91FC5DE272B5EF000F19C3D /* Products */ = { - isa = PBXGroup; - children = ( - F91FC5DD272B5EF000F19C3D /* WorkflowCombineSampleApp.app */, - F9148E552730496400A2A822 /* WorkflowCombineSampleAppUnitTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - F91FC5DF272B5EF000F19C3D /* WorkflowCombineSampleApp */ = { - isa = PBXGroup; - children = ( - F91FC5F5272B5F0500F19C3D /* DemoWorker.swift */, - F91FC5F4272B5F0500F19C3D /* DemoWorkflow.swift */, - F91FC5E0272B5EF000F19C3D /* AppDelegate.swift */, - F91FC5E2272B5EF000F19C3D /* SceneDelegate.swift */, - F91FC5F8272B5FBB00F19C3D /* DemoViewController.swift */, - F91FC5E4272B5EF000F19C3D /* ViewController.swift */, - F91FC5E6272B5EF000F19C3D /* Main.storyboard */, - F91FC5E9272B5EF700F19C3D /* Assets.xcassets */, - F91FC5EB272B5EF700F19C3D /* LaunchScreen.storyboard */, - F91FC5EE272B5EF700F19C3D /* Info.plist */, - ); - path = WorkflowCombineSampleApp; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - F9148E542730496400A2A822 /* WorkflowCombineSampleAppUnitTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F9148E5E2730496400A2A822 /* Build configuration list for PBXNativeTarget "WorkflowCombineSampleAppUnitTests" */; - buildPhases = ( - 910F3B6833A2504DA2EEA54D /* [CP] Check Pods Manifest.lock */, - F9148E512730496400A2A822 /* Sources */, - F9148E522730496400A2A822 /* Frameworks */, - F9148E532730496400A2A822 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - F9148E5B2730496400A2A822 /* PBXTargetDependency */, - ); - name = WorkflowCombineSampleAppUnitTests; - productName = WorkflowCombineSampleAppUnitTests; - productReference = F9148E552730496400A2A822 /* WorkflowCombineSampleAppUnitTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - F91FC5DC272B5EF000F19C3D /* WorkflowCombineSampleApp */ = { - isa = PBXNativeTarget; - buildConfigurationList = F91FC5F1272B5EF700F19C3D /* Build configuration list for PBXNativeTarget "WorkflowCombineSampleApp" */; - buildPhases = ( - BC8BD4A8482F12EE585D17CE /* [CP] Check Pods Manifest.lock */, - F91FC5D9272B5EF000F19C3D /* Sources */, - F91FC5DA272B5EF000F19C3D /* Frameworks */, - F91FC5DB272B5EF000F19C3D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = WorkflowCombineSampleApp; - productName = WorkflowCombineSampleApp; - productReference = F91FC5DD272B5EF000F19C3D /* WorkflowCombineSampleApp.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - F91FC5D5272B5EF000F19C3D /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1250; - LastUpgradeCheck = 1250; - TargetAttributes = { - F9148E542730496400A2A822 = { - CreatedOnToolsVersion = 12.5.1; - TestTargetID = F91FC5DC272B5EF000F19C3D; - }; - F91FC5DC272B5EF000F19C3D = { - CreatedOnToolsVersion = 12.5.1; - }; - }; - }; - buildConfigurationList = F91FC5D8272B5EF000F19C3D /* Build configuration list for PBXProject "WorkflowCombineSampleApp" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = F91FC5D4272B5EF000F19C3D; - productRefGroup = F91FC5DE272B5EF000F19C3D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - F91FC5DC272B5EF000F19C3D /* WorkflowCombineSampleApp */, - F9148E542730496400A2A822 /* WorkflowCombineSampleAppUnitTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - F9148E532730496400A2A822 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F91FC5DB272B5EF000F19C3D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F91FC5ED272B5EF700F19C3D /* LaunchScreen.storyboard in Resources */, - F91FC5EA272B5EF700F19C3D /* Assets.xcassets in Resources */, - F91FC5E8272B5EF000F19C3D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 910F3B6833A2504DA2EEA54D /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-WorkflowCombineSampleAppUnitTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - BC8BD4A8482F12EE585D17CE /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-WorkflowCombineSampleApp-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - F9148E512730496400A2A822 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F9148E582730496400A2A822 /* WorkflowCombineSampleAppUnitTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F91FC5D9272B5EF000F19C3D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F91FC5E5272B5EF000F19C3D /* ViewController.swift in Sources */, - F91FC5F7272B5F0500F19C3D /* DemoWorker.swift in Sources */, - F91FC5F6272B5F0500F19C3D /* DemoWorkflow.swift in Sources */, - F91FC5F9272B5FBB00F19C3D /* DemoViewController.swift in Sources */, - F91FC5E1272B5EF000F19C3D /* AppDelegate.swift in Sources */, - F91FC5E3272B5EF000F19C3D /* SceneDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - F9148E5B2730496400A2A822 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F91FC5DC272B5EF000F19C3D /* WorkflowCombineSampleApp */; - targetProxy = F9148E5A2730496400A2A822 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - F91FC5E6272B5EF000F19C3D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - F91FC5E7272B5EF000F19C3D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - F91FC5EB272B5EF700F19C3D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - F91FC5EC272B5EF700F19C3D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - F9148E5C2730496400A2A822 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DE091C17D6BFBA3117FD439A /* Pods-WorkflowCombineSampleAppUnitTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = WorkflowCombineSampleAppUnitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.soorinpark.parkornapp.WorkflowCombineSampleAppUnitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WorkflowCombineSampleApp.app/WorkflowCombineSampleApp"; - }; - name = Debug; - }; - F9148E5D2730496400A2A822 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 6EC8006ECC2D615830927194 /* Pods-WorkflowCombineSampleAppUnitTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = WorkflowCombineSampleAppUnitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.soorinpark.parkornapp.WorkflowCombineSampleAppUnitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WorkflowCombineSampleApp.app/WorkflowCombineSampleApp"; - }; - name = Release; - }; - F91FC5EF272B5EF700F19C3D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - F91FC5F0272B5EF700F19C3D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - F91FC5F2272B5EF700F19C3D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9CD0CAA819EECAD20D89C730 /* Pods-WorkflowCombineSampleApp.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = WorkflowCombineSampleApp/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.soorinpark.parkornapp.WorkflowCombineSampleApp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - F91FC5F3272B5EF700F19C3D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DCBB19D1BA1D3E7BDFA35AAC /* Pods-WorkflowCombineSampleApp.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = WorkflowCombineSampleApp/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.soorinpark.parkornapp.WorkflowCombineSampleApp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - F9148E5E2730496400A2A822 /* Build configuration list for PBXNativeTarget "WorkflowCombineSampleAppUnitTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F9148E5C2730496400A2A822 /* Debug */, - F9148E5D2730496400A2A822 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F91FC5D8272B5EF000F19C3D /* Build configuration list for PBXProject "WorkflowCombineSampleApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F91FC5EF272B5EF700F19C3D /* Debug */, - F91FC5F0272B5EF700F19C3D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F91FC5F1272B5EF700F19C3D /* Build configuration list for PBXNativeTarget "WorkflowCombineSampleApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F91FC5F2272B5EF700F19C3D /* Debug */, - F91FC5F3272B5EF700F19C3D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = F91FC5D5272B5EF000F19C3D /* Project object */; -} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a62..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/contents.xcworkspacedata b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index c8c598923..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/AppDelegate.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/AppDelegate.swift index ec87c1c79..5e29780c2 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/AppDelegate.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/AppDelegate.swift @@ -6,25 +6,17 @@ // import UIKit +import WorkflowUI -@main +@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } - - // MARK: UISceneSession Lifecycle + var window: UIWindow? - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + window = UIWindow(frame: UIScreen.main.bounds) + window?.rootViewController = ContainerViewController(workflow: DemoWorkflow()) + window?.makeKeyAndVisible() - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + return true } } diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/SceneDelegate.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/SceneDelegate.swift deleted file mode 100644 index 135e6cf16..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/SceneDelegate.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// SceneDelegate.swift -// WorkflowCombineSampleApp -// -// Created by Soo Rin Park on 10/28/21. -// - -import UIKit -import WorkflowUI - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let windowScene = (scene as? UIWindowScene) else { return } - let window = UIWindow(windowScene: windowScene) - let viewController = ContainerViewController(workflow: DemoWorkflow()) - window.rootViewController = viewController - self.window = window - window.makeKeyAndVisible() - } - - func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). - } - - func sceneDidBecomeActive(_ scene: UIScene) { - // Called when the scene has moved from an inactive state to an active state. - // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. - } - - func sceneWillResignActive(_ scene: UIScene) { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). - } - - func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. - } - - func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. - } -} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift index 5de82deab..6d10c76a1 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift @@ -8,7 +8,7 @@ import Combine import Workflow import XCTest -@testable import WorkflowCombineSampleApp +@testable import Development_WorkflowCombineSampleApp class DemoWorkerTests: XCTestCase { func test_workflowIsRenderedEverySecondForFiveSeconds() { From 55db0453e230f2118fbc016994c72e264b945700 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Mon, 1 Nov 2021 13:20:33 -0600 Subject: [PATCH 12/17] handle cancelling cancellable on lifetime ending --- WorkflowCombine/Sources/PublisherWorkflow.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WorkflowCombine/Sources/PublisherWorkflow.swift b/WorkflowCombine/Sources/PublisherWorkflow.swift index f244d1511..1dd0cc36e 100644 --- a/WorkflowCombine/Sources/PublisherWorkflow.swift +++ b/WorkflowCombine/Sources/PublisherWorkflow.swift @@ -42,10 +42,14 @@ public func render(state: State, context: RenderContext) -> Rendering { let sink = context.makeSink(of: AnyWorkflowAction.self) context.runSideEffect(key: "") { [publisher] lifetime in - _ = publisher + let cancellable = publisher .map { AnyWorkflowAction(sendingOutput: $0) } .subscribe(on: RunLoop.main) .sink { sink.send($0) } + + lifetime.onEnded { + cancellable.cancel() + } } } } From d54c04dfece093b5ff225b6193e2fc77db7e12cf Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Mon, 1 Nov 2021 15:53:19 -0600 Subject: [PATCH 13/17] Remove references of WorkflowReactiveSwift from WorkflowCombineSampleApp --- Samples/WorkflowCombineSampleApp/README.md | 48 ------------------- .../WorkflowCombineSampleApp/DemoWorker.swift | 16 ------- 2 files changed, 64 deletions(-) delete mode 100644 Samples/WorkflowCombineSampleApp/README.md diff --git a/Samples/WorkflowCombineSampleApp/README.md b/Samples/WorkflowCombineSampleApp/README.md deleted file mode 100644 index 09bcaacd9..000000000 --- a/Samples/WorkflowCombineSampleApp/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# WorkflowCombineSampleApp - -This sample project utilizes the WorkflowCombine library to demonstrate its usage in a `Workflow`. It is a simple app with a label that updates the current date & time every second. - -# Usage - -Thanks to the `AnyWorkflowConvertible` protocol, both the `WorkflowReactiveSwift` and `WorkflowCombine` Workers have identical api interface. To to migrate your `WorkflowReactiveSwift` `Worker`s to use the `WorkflowCombine` `Worker`s, you will need to do the following: - -1. Change `import` statements to use `WorkflowCombine` and `Combine` -2. Replace `SignalProducer` into `AnyPublisher` for the return type of `run` -3. Change the `run` implementation to the `Combine` equivalence. Most likely this will be the step that that requires the most attention, however if the tests were written for the `Workflow` it can be used to validate the new implementation without the need to change the tests. - -Below is an example of a simple `Worker` in both `WorkflowReactiveSwift` and `WorkflowCombine` that emits a signal every second with the current date. - -### `WorkflowReactiveSwift` - -```swift -struct TimerWorker: Worker { - typealias Output = Date - - func run() -> SignalProducer { - SignalProducer - .timer(interval: DispatchTimeInterval.seconds(1), on: QueueScheduler.main) - } - - func isEquivalent(to otherWorker: TimerWorker2) -> Bool { true } -} -``` - -### `WorkflowCombine` - -```swift -struct TimerWorker: Worker { - typealias Output = Date - - func run() -> AnyPublisher { - Timer.publish(every: 1, on: .main, in: .common) - .autoconnect() - .eraseToAnyPublisher() - } - - func isEquivalent(to otherWorker: Self) -> Bool { true } -} -``` - -### Notes - -This library does **not** remove the usage of the `ReactiveSwift` library from the `Workflow` library. Currently the `Workflow` implementation is tightly coupled with `ReactiveSwift`, and this library is only limited to the `Worker`. Therefore, when utilizing both the existing `Workflow` and the new `Combine` backed `Worker` , you will need to utilize both the `ReactiveSwift` and `Combine` libraries. diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift index 2b7dc0cf9..48fc17dc0 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift @@ -29,19 +29,3 @@ extension DemoWorkflow { func isEquivalent(to otherWorker: DemoWorkflow.DemoWorker) -> Bool { true } } } - -/// Identifcal implementation of the Combine Worker using the WorkflowReactiveSwift library instead. -/// To ensure that both implementations are correct, run the test suite with each implementation uncommented. -// extension DemoWorkflow { -// struct DemoWorker: WorkflowReactiveSwift.Worker { -// typealias Output = Action -// -// func run() -> SignalProducer { -// SignalProducer -// .timer(interval: DispatchTimeInterval.seconds(1), on: QueueScheduler()) -// .map { .init(publishedDate: $0) } -// } -// -// func isEquivalent(to otherWorker: DemoWorkflow.DemoWorker) -> Bool { true } -// } -// } From 2057460434e37f73a356d562aeb42273cb374848 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Tue, 2 Nov 2021 15:48:40 -0600 Subject: [PATCH 14/17] Removed unwanted files --- .gitignore | 4 + .../SampleSwiftUIApp/Info.plist | 60 ------------ Samples/TicTacToe/Tests/Info.plist | 22 ----- .../AccentColor.colorset/Contents.json | 11 --- .../AppIcon.appiconset/Contents.json | 98 ------------------- .../Assets.xcassets/Contents.json | 6 -- .../Base.lproj/LaunchScreen.storyboard | 25 ----- .../Base.lproj/Main.storyboard | 24 ----- .../WorkflowCombineSampleApp/DemoWorker.swift | 2 - .../DemoWorkflow.swift | 2 +- .../WorkflowCombineSampleApp/Info.plist | 66 ------------- .../ViewController.swift | 15 --- .../DemoWorkflowTests.swift | 24 +++++ .../Info.plist | 22 ----- .../WorkflowCombineSampleAppUnitTests.swift | 28 ------ 15 files changed, 29 insertions(+), 380 deletions(-) delete mode 100644 Samples/SampleSwiftUIApp/SampleSwiftUIApp/Info.plist delete mode 100644 Samples/TicTacToe/Tests/Info.plist delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AccentColor.colorset/Contents.json delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/Contents.json delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/LaunchScreen.storyboard delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/Main.storyboard delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Info.plist delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/ViewController.swift create mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/DemoWorkflowTests.swift delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/Info.plist delete mode 100644 Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift diff --git a/.gitignore b/.gitignore index 1c44ba6f2..495ddcb39 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ SampleApp.xcworkspace # ios-snapshot-test-case Failure Diffs FailureDiffs/ + +Samples/**/*Info.plist +!Samples/Tutorial/AppHost/Configuration/Info.plist +!Samples/Tutorial/AppHost/TutorialTests/Info.plist \ No newline at end of file diff --git a/Samples/SampleSwiftUIApp/SampleSwiftUIApp/Info.plist b/Samples/SampleSwiftUIApp/SampleSwiftUIApp/Info.plist deleted file mode 100644 index 41456fbdd..000000000 --- a/Samples/SampleSwiftUIApp/SampleSwiftUIApp/Info.plist +++ /dev/null @@ -1,60 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - - - - - UILaunchStoryboardName - - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/Samples/TicTacToe/Tests/Info.plist b/Samples/TicTacToe/Tests/Info.plist deleted file mode 100644 index 64d65ca49..000000000 --- a/Samples/TicTacToe/Tests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AccentColor.colorset/Contents.json b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb8789700..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 9221b9bb1..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/Contents.json b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596a..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/LaunchScreen.storyboard b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e9329f..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/Main.storyboard b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/Main.storyboard deleted file mode 100644 index 25a763858..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Base.lproj/Main.storyboard +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift index 48fc17dc0..ede21d374 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift @@ -6,10 +6,8 @@ // import Combine -import ReactiveSwift import Workflow import WorkflowCombine -import WorkflowReactiveSwift import WorkflowUI // MARK: Workers diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift index bc1e286ac..b94eb8855 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorkflow.swift @@ -19,7 +19,7 @@ struct DemoWorkflow: Workflow { // MARK: State and Initialization extension DemoWorkflow { - struct State { + struct State: Equatable { var date: Date } diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Info.plist b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Info.plist deleted file mode 100644 index 5b531f7b2..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/Info.plist +++ /dev/null @@ -1,66 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - UISceneStoryboardFile - Main - - - - - UIApplicationSupportsIndirectInputEvents - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/ViewController.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/ViewController.swift deleted file mode 100644 index f582053e8..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/ViewController.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// ViewController.swift -// WorkflowCombineSampleApp -// -// Created by Soo Rin Park on 10/28/21. -// - -import UIKit - -class ViewController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. - } -} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/DemoWorkflowTests.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/DemoWorkflowTests.swift new file mode 100644 index 000000000..80bba0359 --- /dev/null +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/DemoWorkflowTests.swift @@ -0,0 +1,24 @@ +// +// DemoWorkflowTests.swift +// WorkflowCombineSampleAppUnitTests +// +// Created by Soo Rin Park on 11/1/21. +// + +import Combine +import Workflow +import WorkflowTesting +import XCTest +@testable import Development_WorkflowCombineSampleApp + +class DemoWorkflowTests: XCTestCase { + func test_workflowIsRenderedEverySecondForFiveSeconds() { + let expectedDate = Date(timeIntervalSince1970: 0) + + DemoWorkflow + .Action + .tester(withState: .init(date: Date())) // the initial date itself does not matter + .send(action: .init(publishedDate: expectedDate)) + .assert(state: .init(date: expectedDate)) + } +} diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/Info.plist b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/Info.plist deleted file mode 100644 index 64d65ca49..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift deleted file mode 100644 index 6d10c76a1..000000000 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/WorkflowCombineSampleAppUnitTests.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// WorkflowCombineSampleAppUnitTests.swift -// WorkflowCombineSampleAppUnitTests -// -// Created by Soo Rin Park on 11/1/21. -// - -import Combine -import Workflow -import XCTest -@testable import Development_WorkflowCombineSampleApp - -class DemoWorkerTests: XCTestCase { - func test_workflowIsRenderedEverySecondForFiveSeconds() { - let host = WorkflowHost(workflow: DemoWorkflow()) - - let expectation = XCTestExpectation(description: "host rendering is updated every second") - expectation.expectedFulfillmentCount = 5 - let disposable = host.rendering.signal.observeValues { rendering in - print(rendering) - expectation.fulfill() - } - - // buffer milisecond is added to account for the workflow to start running - wait(for: [expectation], timeout: 5.1) - disposable?.dispose() - } -} From 2b727dfd231ca66807263936cd8bc657ffbb6fda Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Tue, 2 Nov 2021 17:32:53 -0600 Subject: [PATCH 15/17] Fix Workflows to take a generic publisher type --- .../WorkflowCombineSampleApp/DemoWorker.swift | 4 ++-- WorkflowCombine/Sources/PublisherWorkflow.swift | 9 +++++---- WorkflowCombine/Sources/Worker.swift | 7 ++++--- WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift | 3 ++- WorkflowCombine/Tests/TestingTests.swift | 7 +++++-- WorkflowCombine/Tests/WorkerTests.swift | 5 ++++- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift index ede21d374..b0e3f6734 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift @@ -17,10 +17,10 @@ extension DemoWorkflow { typealias Output = Action // This publisher publishes the current date on a timer that fires every second - func run() -> AnyPublisher { + func run() -> AnyPublisher { Timer.publish(every: 1, on: .main, in: .common) .autoconnect() - .map { .init(publishedDate: $0) } + .map { Action(publishedDate: $0) } .eraseToAnyPublisher() } diff --git a/WorkflowCombine/Sources/PublisherWorkflow.swift b/WorkflowCombine/Sources/PublisherWorkflow.swift index 1dd0cc36e..6ca9ce2e4 100644 --- a/WorkflowCombine/Sources/PublisherWorkflow.swift +++ b/WorkflowCombine/Sources/PublisherWorkflow.swift @@ -21,6 +21,7 @@ import Workflow @available(iOS 13.0, macOS 10.15, *) + extension AnyPublisher: AnyWorkflowConvertible where Failure == Never { public func asAnyWorkflow() -> AnyWorkflow { return PublisherWorkflow(publisher: self).asAnyWorkflow() @@ -28,14 +29,14 @@ } @available(iOS 13.0, macOS 10.15, *) - struct PublisherWorkflow: Workflow { - public typealias Output = Value + struct PublisherWorkflow: Workflow where WorkflowPublisher.Failure == Never { + public typealias Output = WorkflowPublisher.Output public typealias State = Void public typealias Rendering = Void - let publisher: AnyPublisher + let publisher: WorkflowPublisher - public init(publisher: AnyPublisher) { + public init(publisher: WorkflowPublisher) { self.publisher = publisher } diff --git a/WorkflowCombine/Sources/Worker.swift b/WorkflowCombine/Sources/Worker.swift index 930d023f0..a90d53d59 100644 --- a/WorkflowCombine/Sources/Worker.swift +++ b/WorkflowCombine/Sources/Worker.swift @@ -32,10 +32,11 @@ public protocol Worker: AnyWorkflowConvertible where Rendering == Void { /// The type of output events returned by this worker. associatedtype Output + associatedtype WorkerPublisher: Publisher where + WorkerPublisher.Output == Output, WorkerPublisher.Failure == Never /// Returns a publisher to execute the work represented by this worker. - func run() -> AnyPublisher - + func run() -> WorkerPublisher /// Returns `true` if the other worker should be considered equivalent to `self`. Equivalence should take into /// account whatever data is meaningful to the task. For example, a worker that loads a user account from a server /// would not be equivalent to another worker with a different user ID. @@ -85,7 +86,7 @@ logger.logFinished(status: "Cancelled") } ) - .map { AnyWorkflowAction(sendingOutput: $0) } + .map { AnyWorkflowAction(sendingOutput: $0) } } .eraseToAnyPublisher() .running(in: context, key: state.uuidString) diff --git a/WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift b/WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift index 788e4585f..3629bd1e5 100644 --- a/WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift +++ b/WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift @@ -16,6 +16,7 @@ #if DEBUG + import Combine import Workflow import WorkflowTesting import XCTest @@ -35,7 +36,7 @@ file: StaticString = #file, line: UInt = #line ) -> RenderTester { expectWorkflow( - type: PublisherWorkflow.self, + type: PublisherWorkflow>.self, key: key, producingRendering: (), producingOutput: output, diff --git a/WorkflowCombine/Tests/TestingTests.swift b/WorkflowCombine/Tests/TestingTests.swift index 25ca6526a..c78a13c51 100644 --- a/WorkflowCombine/Tests/TestingTests.swift +++ b/WorkflowCombine/Tests/TestingTests.swift @@ -183,10 +183,13 @@ private struct TestWorkflow: Workflow { } private struct TestWorker: Worker { + typealias Output = String + typealias WorkerPublisher = Just + let input: String - func run() -> AnyPublisher { - Just("").eraseToAnyPublisher() + func run() -> WorkerPublisher { + Just("") } func isEquivalent(to otherWorker: TestWorker) -> Bool { diff --git a/WorkflowCombine/Tests/WorkerTests.swift b/WorkflowCombine/Tests/WorkerTests.swift index c3930b693..9a86b5e1f 100644 --- a/WorkflowCombine/Tests/WorkerTests.swift +++ b/WorkflowCombine/Tests/WorkerTests.swift @@ -136,11 +136,14 @@ class WorkerTests: XCTestCase { } struct TestWorker: Worker { + typealias Output = WorkerPublisher.Output + typealias WorkerPublisher = AnyPublisher + func isEquivalent(to otherWorker: TestWorker) -> Bool { true } - func run() -> AnyPublisher { + func run() -> WorkerPublisher { [1, 2].publisher .delay(for: .milliseconds(1), scheduler: RunLoop.main) .eraseToAnyPublisher() From 07fd80a40660d8ead76203424a2bc2a35d65082a Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Wed, 3 Nov 2021 10:36:53 -0600 Subject: [PATCH 16/17] Remove the need to type-erase publisher to render the workflow view --- Development.podspec | 22 ------------ .../WorkflowCombineSampleApp/DemoWorker.swift | 3 +- .../DemoWorkflowTests.swift | 2 +- .../Sources/Publisher+Extensions.swift | 34 +++++++++++++++++++ .../Sources/PublisherWorkflow.swift | 8 ----- WorkflowCombine/Sources/Worker.swift | 1 - ...erTesting.swift => PublisherTesting.swift} | 12 +++---- ...lisherTests.swift => PublisherTests.swift} | 11 +++--- WorkflowCombine/Tests/WorkerTests.swift | 13 +++---- 9 files changed, 51 insertions(+), 55 deletions(-) create mode 100644 WorkflowCombine/Sources/Publisher+Extensions.swift rename WorkflowCombine/Tests/Helpers/{AnyPublisherTesting.swift => PublisherTesting.swift} (80%) rename WorkflowCombine/Tests/{AnyPublisherTests.swift => PublisherTests.swift} (90%) diff --git a/Development.podspec b/Development.podspec index decdcafc6..274cf3b08 100644 --- a/Development.podspec +++ b/Development.podspec @@ -13,7 +13,6 @@ Pod::Spec.new do |s| s.dependency 'WorkflowUI' s.dependency 'WorkflowReactiveSwift' s.dependency 'WorkflowRxSwift' - # s.dependency 'WorkflowCombine' # TODO: Disabled because app specs cannot increase the deployment target of the root s.source_files = 'Samples/Dummy.swift' s.subspec 'Dummy' do |ss| @@ -142,25 +141,4 @@ Pod::Spec.new do |s| test_spec.dependency 'WorkflowTesting' test_spec.dependency 'WorkflowRxSwiftTesting' end - - # TODO: Disabled because app specs cannot increase the deployment target of the root - # To use, increase the deployment target of this spec to 13.0 or higher - # s.app_spec 'WorkflowCombineSampleApp' do |app_spec| - # app_spec.source_files = 'Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/**/*.swift' - # end - - # s.test_spec 'WorkflowCombineSampleAppTests' do |test_spec| - # test_spec.dependency 'Development/WorkflowCombineSampleApp' - # test_spec.requires_app_host = true - # test_spec.app_host_name = 'Development/WorkflowCombineSampleApp' - # test_spec.source_files = 'Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/**/*.swift' - # end -# - # s.test_spec 'WorkflowCombineTests' do |test_spec| - # test_spec.requires_app_host = true - # test_spec.source_files = 'WorkflowCombine/Tests/**/*.swift' - # test_spec.framework = 'XCTest' - # test_spec.dependency 'WorkflowTesting' - # test_spec.dependency 'WorkflowCombineTesting' - # end end diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift index b0e3f6734..0721ceeee 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/DemoWorker.swift @@ -8,7 +8,6 @@ import Combine import Workflow import WorkflowCombine -import WorkflowUI // MARK: Workers @@ -17,7 +16,7 @@ extension DemoWorkflow { typealias Output = Action // This publisher publishes the current date on a timer that fires every second - func run() -> AnyPublisher { + func run() -> AnyPublisher { Timer.publish(every: 1, on: .main, in: .common) .autoconnect() .map { Action(publishedDate: $0) } diff --git a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/DemoWorkflowTests.swift b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/DemoWorkflowTests.swift index 80bba0359..1ee01c4fb 100644 --- a/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/DemoWorkflowTests.swift +++ b/Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/DemoWorkflowTests.swift @@ -12,7 +12,7 @@ import XCTest @testable import Development_WorkflowCombineSampleApp class DemoWorkflowTests: XCTestCase { - func test_workflowIsRenderedEverySecondForFiveSeconds() { + func test_demoWorkflow_publishesNewDate() { let expectedDate = Date(timeIntervalSince1970: 0) DemoWorkflow diff --git a/WorkflowCombine/Sources/Publisher+Extensions.swift b/WorkflowCombine/Sources/Publisher+Extensions.swift new file mode 100644 index 000000000..d2b4c0e09 --- /dev/null +++ b/WorkflowCombine/Sources/Publisher+Extensions.swift @@ -0,0 +1,34 @@ +// +// Publisher+Extensions.swift +// WorkflowCombine +// +// Created by Soo Rin Park on 11/3/21. +// + +#if canImport(Combine) && swift(>=5.1) + + import Combine + import Foundation + import Workflow + + @available(iOS 13.0, macOS 10.15, *) + /// This is a workaround to the fact you extensions of protocols cannot have an inheritance clause. + /// a previous solution had extending the `AnyPublisher` to conform to `AnyWorkflowConvertible`, + /// but was limited in the fact that rendering was only available to `AnyPublisher`s. + /// this solutions makes it so that all publishers can render its view. + extension Publisher where Failure == Never { + public func running(in context: RenderContext, key: String = "") where + Output == AnyWorkflowAction { + asAnyWorkflow().rendered(in: context, key: key, outputMap: { $0 }) + } + + public func mapOutput(_ transform: @escaping (Output) -> NewOutput) -> AnyWorkflow { + asAnyWorkflow().mapOutput(transform) + } + + func asAnyWorkflow() -> AnyWorkflow { + PublisherWorkflow(publisher: self).asAnyWorkflow() + } + } + +#endif diff --git a/WorkflowCombine/Sources/PublisherWorkflow.swift b/WorkflowCombine/Sources/PublisherWorkflow.swift index 6ca9ce2e4..8bed90a07 100644 --- a/WorkflowCombine/Sources/PublisherWorkflow.swift +++ b/WorkflowCombine/Sources/PublisherWorkflow.swift @@ -20,14 +20,6 @@ import Foundation import Workflow - @available(iOS 13.0, macOS 10.15, *) - - extension AnyPublisher: AnyWorkflowConvertible where Failure == Never { - public func asAnyWorkflow() -> AnyWorkflow { - return PublisherWorkflow(publisher: self).asAnyWorkflow() - } - } - @available(iOS 13.0, macOS 10.15, *) struct PublisherWorkflow: Workflow where WorkflowPublisher.Failure == Never { public typealias Output = WorkflowPublisher.Output diff --git a/WorkflowCombine/Sources/Worker.swift b/WorkflowCombine/Sources/Worker.swift index a90d53d59..d80783882 100644 --- a/WorkflowCombine/Sources/Worker.swift +++ b/WorkflowCombine/Sources/Worker.swift @@ -88,7 +88,6 @@ ) .map { AnyWorkflowAction(sendingOutput: $0) } } - .eraseToAnyPublisher() .running(in: context, key: state.uuidString) } } diff --git a/WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift b/WorkflowCombine/Tests/Helpers/PublisherTesting.swift similarity index 80% rename from WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift rename to WorkflowCombine/Tests/Helpers/PublisherTesting.swift index 3629bd1e5..c415c8938 100644 --- a/WorkflowCombine/Tests/Helpers/AnyPublisherTesting.swift +++ b/WorkflowCombine/Tests/Helpers/PublisherTesting.swift @@ -30,13 +30,13 @@ /// - Parameters: /// - producingOutput: An output that should be returned when this worker is requested, if any. /// - key: Key to expect this `Workflow` to be rendered with. - public func expectPublisher( - producingOutput output: OutputType? = nil, - key: String = "", - file: StaticString = #file, line: UInt = #line - ) -> RenderTester { + public func expectPublisher( + publisher: PublisherType.Type, + output: PublisherType.Output, + key: String = "" + ) -> RenderTester where PublisherType.Failure == Never { expectWorkflow( - type: PublisherWorkflow>.self, + type: PublisherWorkflow.self, key: key, producingRendering: (), producingOutput: output, diff --git a/WorkflowCombine/Tests/AnyPublisherTests.swift b/WorkflowCombine/Tests/PublisherTests.swift similarity index 90% rename from WorkflowCombine/Tests/AnyPublisherTests.swift rename to WorkflowCombine/Tests/PublisherTests.swift index ad63a0e36..41489d6b7 100644 --- a/WorkflowCombine/Tests/AnyPublisherTests.swift +++ b/WorkflowCombine/Tests/PublisherTests.swift @@ -20,11 +20,11 @@ import Workflow import XCTest @testable import WorkflowCombine -class AnyPublisherTests: XCTestCase { +class PublisherTests: XCTestCase { func testPublisherWorkflow() { TestWorkflow() .renderTester() - .expectPublisher(producingOutput: 1, key: "123") + .expectPublisher(publisher: Publishers.Sequence<[Int], Never>.self, output: 1, key: "123") .render {} } @@ -34,14 +34,13 @@ class AnyPublisherTests: XCTestCase { func render(state: State, context: RenderContext) -> Rendering { [1].publisher - .eraseToAnyPublisher() .mapOutput { _ in AnyWorkflowAction.noAction } .running(in: context, key: "123") } } func test_publisherWorkflow_usesSideEffectWithKey() { - let publisher = Just(1).eraseToAnyPublisher() + let publisher = Just(1) PublisherWorkflow(publisher: publisher) .renderTester() .expectSideEffect(key: "") @@ -49,7 +48,7 @@ class AnyPublisherTests: XCTestCase { } func test_output() { - let publisher = Just(1).eraseToAnyPublisher() + let publisher = Just(1) let host = WorkflowHost( workflow: PublisherWorkflow(publisher: publisher) @@ -72,7 +71,7 @@ class AnyPublisherTests: XCTestCase { let publisher = [1, 2, 3].publisher let host = WorkflowHost( - workflow: PublisherWorkflow(publisher: publisher.eraseToAnyPublisher()) + workflow: PublisherWorkflow(publisher: publisher) ) let expectation = XCTestExpectation() diff --git a/WorkflowCombine/Tests/WorkerTests.swift b/WorkflowCombine/Tests/WorkerTests.swift index 9a86b5e1f..f1cf8a3cf 100644 --- a/WorkflowCombine/Tests/WorkerTests.swift +++ b/WorkflowCombine/Tests/WorkerTests.swift @@ -136,14 +136,13 @@ class WorkerTests: XCTestCase { } struct TestWorker: Worker { - typealias Output = WorkerPublisher.Output - typealias WorkerPublisher = AnyPublisher + typealias Output = Int func isEquivalent(to otherWorker: TestWorker) -> Bool { true } - func run() -> WorkerPublisher { + func run() -> AnyPublisher { [1, 2].publisher .delay(for: .milliseconds(1), scheduler: RunLoop.main) .eraseToAnyPublisher() @@ -194,11 +193,7 @@ private struct PublisherTestWorkflow: Workflow { private struct PublisherTestWorker: Worker { typealias Output = Int - func run() -> AnyPublisher { - Just(1).eraseToAnyPublisher() - } + func run() -> Just { Just(1) } - func isEquivalent(to otherWorker: PublisherTestWorker) -> Bool { - true - } + func isEquivalent(to otherWorker: PublisherTestWorker) -> Bool { true } } From c4d4d102dd93ac223bb523becb7076e3401c7c28 Mon Sep 17 00:00:00 2001 From: Soo Rin Park Date: Wed, 3 Nov 2021 15:10:06 -0600 Subject: [PATCH 17/17] Separate WorkflowCombineTesting module --- Development.podspec | 31 +++++++++++++++++ .../PublisherTesting.swift | 0 .../Helpers => Testing}/WorkerTesting.swift | 2 -- .../TestingTests/PublisherTests.swift | 33 +++++++++++++++++++ .../TestingTests.swift | 9 ++--- WorkflowCombine/Tests/PublisherTests.swift | 26 ++------------- 6 files changed, 70 insertions(+), 31 deletions(-) rename WorkflowCombine/{Tests/Helpers => Testing}/PublisherTesting.swift (100%) rename WorkflowCombine/{Tests/Helpers => Testing}/WorkerTesting.swift (99%) create mode 100644 WorkflowCombine/TestingTests/PublisherTests.swift rename WorkflowCombine/{Tests => TestingTests}/TestingTests.swift (97%) diff --git a/Development.podspec b/Development.podspec index 274cf3b08..cd94e5ad5 100644 --- a/Development.podspec +++ b/Development.podspec @@ -13,6 +13,7 @@ Pod::Spec.new do |s| s.dependency 'WorkflowUI' s.dependency 'WorkflowReactiveSwift' s.dependency 'WorkflowRxSwift' + # s.dependency 'WorkflowCombine' # TODO: Disabled because app specs cannot increase the deployment target of the root s.source_files = 'Samples/Dummy.swift' s.subspec 'Dummy' do |ss| @@ -141,4 +142,34 @@ Pod::Spec.new do |s| test_spec.dependency 'WorkflowTesting' test_spec.dependency 'WorkflowRxSwiftTesting' end + + # TODO: Disabled because app specs cannot increase the deployment target of the root + # To use, increase the deployment target of this spec to 13.0 or higher + # s.app_spec 'WorkflowCombineSampleApp' do |app_spec| + # app_spec.source_files = 'Samples/WorkflowCombineSampleApp/WorkflowCombineSampleApp/**/*.swift' + # end + # + # s.test_spec 'WorkflowCombineSampleAppTests' do |test_spec| + # test_spec.dependency 'Development/WorkflowCombineSampleApp' + # test_spec.dependency 'WorkflowTesting' + # test_spec.requires_app_host = true + # test_spec.app_host_name = 'Development/WorkflowCombineSampleApp' + # test_spec.source_files = 'Samples/WorkflowCombineSampleApp/WorkflowCombineSampleAppUnitTests/**/*.swift' + # end + + # s.test_spec 'WorkflowCombineTests' do |test_spec| + # test_spec.requires_app_host = true + # test_spec.source_files = 'WorkflowCombine/Tests/**/*.swift' + # test_spec.framework = 'XCTest' + # test_spec.dependency 'WorkflowTesting' + # test_spec.dependency 'WorkflowCombineTesting' + # end + + # s.test_spec 'WorkflowCombineTestingTests' do |test_spec| + # test_spec.requires_app_host = true + # test_spec.source_files = 'WorkflowCombine/TestingTests/**/*.swift' + # test_spec.framework = 'XCTest' + # test_spec.dependency 'WorkflowTesting' + # test_spec.dependency 'WorkflowCombineTesting' + # end end diff --git a/WorkflowCombine/Tests/Helpers/PublisherTesting.swift b/WorkflowCombine/Testing/PublisherTesting.swift similarity index 100% rename from WorkflowCombine/Tests/Helpers/PublisherTesting.swift rename to WorkflowCombine/Testing/PublisherTesting.swift diff --git a/WorkflowCombine/Tests/Helpers/WorkerTesting.swift b/WorkflowCombine/Testing/WorkerTesting.swift similarity index 99% rename from WorkflowCombine/Tests/Helpers/WorkerTesting.swift rename to WorkflowCombine/Testing/WorkerTesting.swift index 92dfcbc20..d37e3e5b3 100644 --- a/WorkflowCombine/Tests/Helpers/WorkerTesting.swift +++ b/WorkflowCombine/Testing/WorkerTesting.swift @@ -15,7 +15,6 @@ */ #if DEBUG - import Workflow import WorkflowTesting import XCTest @@ -52,5 +51,4 @@ ) } } - #endif diff --git a/WorkflowCombine/TestingTests/PublisherTests.swift b/WorkflowCombine/TestingTests/PublisherTests.swift new file mode 100644 index 000000000..d9f9e3bb7 --- /dev/null +++ b/WorkflowCombine/TestingTests/PublisherTests.swift @@ -0,0 +1,33 @@ +// +// PublisherTests.swift +// WorkflowCombine +// +// Created by Soo Rin Park on 11/3/21. +// + +import Combine +import Foundation +import Workflow +import WorkflowTesting +import XCTest +@testable import WorkflowCombineTesting + +class PublisherTests: XCTestCase { + func testPublisherWorkflow() { + TestWorkflow() + .renderTester() + .expectPublisher(publisher: Publishers.Sequence<[Int], Never>.self, output: 1, key: "123") + .render {} + } + + struct TestWorkflow: Workflow { + typealias State = Void + typealias Rendering = Void + + func render(state: State, context: RenderContext) -> Rendering { + [1].publisher + .mapOutput { _ in AnyWorkflowAction.noAction } + .running(in: context, key: "123") + } + } +} diff --git a/WorkflowCombine/Tests/TestingTests.swift b/WorkflowCombine/TestingTests/TestingTests.swift similarity index 97% rename from WorkflowCombine/Tests/TestingTests.swift rename to WorkflowCombine/TestingTests/TestingTests.swift index c78a13c51..679d17d8b 100644 --- a/WorkflowCombine/Tests/TestingTests.swift +++ b/WorkflowCombine/TestingTests/TestingTests.swift @@ -17,6 +17,7 @@ import Combine import Workflow import WorkflowCombine +import WorkflowCombineTesting import WorkflowTesting import XCTest @@ -188,11 +189,7 @@ private struct TestWorker: Worker { let input: String - func run() -> WorkerPublisher { - Just("") - } + func run() -> WorkerPublisher { Just(input) } - func isEquivalent(to otherWorker: TestWorker) -> Bool { - input == otherWorker.input - } + func isEquivalent(to otherWorker: TestWorker) -> Bool { input == otherWorker.input } } diff --git a/WorkflowCombine/Tests/PublisherTests.swift b/WorkflowCombine/Tests/PublisherTests.swift index 41489d6b7..e4984571b 100644 --- a/WorkflowCombine/Tests/PublisherTests.swift +++ b/WorkflowCombine/Tests/PublisherTests.swift @@ -17,41 +17,21 @@ import Combine import Foundation import Workflow +import WorkflowCombineTesting import XCTest @testable import WorkflowCombine class PublisherTests: XCTestCase { - func testPublisherWorkflow() { - TestWorkflow() - .renderTester() - .expectPublisher(publisher: Publishers.Sequence<[Int], Never>.self, output: 1, key: "123") - .render {} - } - - struct TestWorkflow: Workflow { - typealias State = Void - typealias Rendering = Void - - func render(state: State, context: RenderContext) -> Rendering { - [1].publisher - .mapOutput { _ in AnyWorkflowAction.noAction } - .running(in: context, key: "123") - } - } - func test_publisherWorkflow_usesSideEffectWithKey() { - let publisher = Just(1) - PublisherWorkflow(publisher: publisher) + PublisherWorkflow(publisher: Just(1)) .renderTester() .expectSideEffect(key: "") .render { _ in } } func test_output() { - let publisher = Just(1) - let host = WorkflowHost( - workflow: PublisherWorkflow(publisher: publisher) + workflow: PublisherWorkflow(publisher: Just(1)) ) let expectation = XCTestExpectation()