diff --git a/CHANGELOG.md b/CHANGELOG.md
index 588c56c..6d8cb70 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+**version 0.18.0**:
+
+* breaking: Executers are no longer associated to a Reducer but to the whole Spin instead (and can still be overriden in each Feedback)
+
**version 0.17.0**:
* introduce Gear: a mediator pattern between Spins that allows them to communicate together
diff --git a/Cartfile b/Cartfile
index 882c76b..15690cd 100644
--- a/Cartfile
+++ b/Cartfile
@@ -1,2 +1,2 @@
github "ReactiveX/RxSwift" ~> 5.1.1
-github "ReactiveCocoa/ReactiveSwift" ~> 6.1
+github "ReactiveCocoa/ReactiveSwift" ~> 6.3.0
diff --git a/Cartfile.resolved b/Cartfile.resolved
index 89d8abd..177d54e 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -1,2 +1,2 @@
-github "ReactiveCocoa/ReactiveSwift" "6.2.1"
+github "ReactiveCocoa/ReactiveSwift" "6.3.0"
github "ReactiveX/RxSwift" "5.1.1"
diff --git a/Package.resolved b/Package.resolved
index 1a321b3..7f3ee25 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -1,49 +1,13 @@
{
"object": {
"pins": [
- {
- "package": "CwlCatchException",
- "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git",
- "state": {
- "branch": null,
- "revision": "7cd2f8cacc4d22f21bc0b2309c3b18acf7957b66",
- "version": "1.2.0"
- }
- },
- {
- "package": "CwlPreconditionTesting",
- "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git",
- "state": {
- "branch": null,
- "revision": "c228db5d2ad1b01ebc84435e823e6cca4e3db98b",
- "version": "1.2.0"
- }
- },
- {
- "package": "Nimble",
- "repositoryURL": "https://github.com/Quick/Nimble.git",
- "state": {
- "branch": null,
- "revision": "b02b00b30b6353632aa4a5fb6124f8147f7140c0",
- "version": "8.0.5"
- }
- },
- {
- "package": "Quick",
- "repositoryURL": "https://github.com/Quick/Quick.git",
- "state": {
- "branch": null,
- "revision": "33682c2f6230c60614861dfc61df267e11a1602f",
- "version": "2.2.0"
- }
- },
{
"package": "ReactiveSwift",
"repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift",
"state": {
"branch": null,
- "revision": "e27ccdbf4ec36f154b60b91a0d7e0110c4e882cb",
- "version": "6.2.1"
+ "revision": "3f4351d04115fd8797802d9b2d17b812cd761602",
+ "version": "6.3.0"
}
},
{
@@ -51,8 +15,8 @@
"repositoryURL": "https://github.com/ReactiveX/RxSwift",
"state": {
"branch": null,
- "revision": "c1bd31b397d87a54467af4161dde9d6b27720c19",
- "version": "5.1.0"
+ "revision": "002d325b0bdee94e7882e1114af5ff4fe1e96afa",
+ "version": "5.1.1"
}
}
]
diff --git a/Package.swift b/Package.swift
index 888a7eb..65c4318 100644
--- a/Package.swift
+++ b/Package.swift
@@ -27,8 +27,8 @@ let package = Package(
targets: ["SpinRxSwift"]),
],
dependencies: [
- .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.2.1"),
- .package(url: "https://github.com/ReactiveX/RxSwift", from: "5.1.0"),
+ .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.3.0"),
+ .package(url: "https://github.com/ReactiveX/RxSwift", from: "5.1.1"),
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
diff --git a/README.md b/README.md
index 099a5da..9f9fc7c 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
-**With the recent introduction of Combine and SwiftUI, we will face some transition periods in our code base. Our applications will use both Combine and a third-party reactive framework, or both UIKit and SwiftUI, which makes it potentially difficult to guarantee a consistent architecture over time.**
+**With the introduction of Combine and SwiftUI, we will face some transition periods in our code base. Our applications will use both Combine and a third-party reactive framework, or both UIKit and SwiftUI, which makes it potentially difficult to guarantee a consistent architecture over time.**
**Spin is a tool to build feedback loops within a Swift based application allowing you to use a unified syntax whatever the underlying reactive programming framework and whatever Apple UI technology you use (RxSwift, ReactiveSwift, Combine and UIKit, AppKit, SwiftUI).**
@@ -251,11 +251,13 @@ Choose wisely the option that fits your needs. Not cancelling previous operation
Reactive programming is often associated with asynchronous execution. Even though every reactive framework comes with its own GCD abstraction, it is always about stating which scheduler the side effect should be executed on.
-Spin provides a way to specify this scheduler for each feedback you add to a loop while still being as declarative as possible:
+By default, a Spin will be executed on a background thread created by the framework.
+
+However, Spin provides a way to specify a scheduler for the Spin it-self and for each feedback you add to it:
```swift
Spinner
- .initialState(Levels(left: 10, right: 20))
+ .initialState(Levels(left: 10, right: 20), executeOn: MainScheduler.instance)
.feedback(Feedback(effect: leftEffect, on: SerialDispatchQueueScheduler(qos: .userInitiated)))
.feedback(Feedback(effect: rightEffect, on: SerialDispatchQueueScheduler(qos: .userInitiated)))
.reducer(Reducer(levelsReducer))
@@ -263,7 +265,7 @@ Spinner
or
```swift
-Spin(initialState: Levels(left: 10, right: 20)) {
+Spin(initialState: Levels(left: 10, right: 20), executeOn: MainScheduler.instance) {
Feedback(effect: leftEffect)
.execute(on: SerialDispatchQueueScheduler(qos: .userInitiated))
Feedback(effect: rightEffect)
@@ -486,7 +488,7 @@ https://github.com/Spinners/Spin.Swift.git
Add the following entry to your Cartfile:
```
-github "Spinners/Spin.Swift" ~> 0.17.0
+github "Spinners/Spin.Swift" ~> 0.18.0
```
and then:
@@ -500,9 +502,9 @@ carthage update Spin.Swift
Add the following dependencies to your Podfile:
```
-pod 'SpinReactiveSwift', '~> 0.17.0'
-pod 'SpinCombine', '~> 0.17.0'
-pod 'SpinRxSwift', '~> 0.17.0'
+pod 'SpinReactiveSwift', '~> 0.18.0'
+pod 'SpinCombine', '~> 0.18.0'
+pod 'SpinRxSwift', '~> 0.18.0'
```
You should then be able to import SpinCommon (base implementation), SpinRxSwift, SpinReactiveSwift or SpinCombine
diff --git a/Sources/Combine/AnyCancellable+DisposeBag.swift b/Sources/Combine/AnyCancellable+DisposeBag.swift
deleted file mode 100644
index 72bb043..0000000
--- a/Sources/Combine/AnyCancellable+DisposeBag.swift
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-// AnyCancellable+DisposeBag.swift
-//
-//
-// Created by Thibault Wittemberg on 2019-12-30.
-//
-
-import Combine
-
-@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public extension AnyCancellable {
- func disposed(by disposables: inout [AnyCancellable]) {
- self.store(in: &disposables)
- }
-}
diff --git a/Sources/Combine/AnyPublisher+streamFromSpin.swift b/Sources/Combine/AnyPublisher+streamFromSpin.swift
index 67788f7..48a8b21 100644
--- a/Sources/Combine/AnyPublisher+streamFromSpin.swift
+++ b/Sources/Combine/AnyPublisher+streamFromSpin.swift
@@ -7,29 +7,36 @@
import Combine
import SpinCommon
+import Dispatch
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public extension AnyPublisher where Failure == Never {
- static func stream(from spin: Spin) -> AnyPublisher {
- return Deferred> { [weak spin] in
+ static func stream(from spin: ScheduledSpin) -> AnyPublisher
+ where
+ Executer: ExecuterDefinition,
+ Executer.Executer: Scheduler {
+ return Deferred> { [weak spin] in
- guard let spin = spin else { return Empty().eraseToAnyPublisher() }
+ guard let spin = spin else { return Empty().eraseToAnyPublisher() }
- let currentState = CurrentValueSubject(spin.initialState)
+ let currentState = CurrentValueSubject(spin.initialState)
- // merging all the effects into one event stream
- let eventStreams = spin.effects.map { $0(currentState.eraseToAnyPublisher()) }
- let eventStream = Publishers.MergeMany(eventStreams).eraseToAnyPublisher()
+ // merging all the effects into one event stream
+ let stateInputStream = currentState.eraseToAnyPublisher()
+ let eventStreams = spin.effects.map { $0(stateInputStream) }
+ let eventStream = Publishers.MergeMany(eventStreams).eraseToAnyPublisher()
- return spin
- .scheduledReducer(eventStream)
- .prepend(spin.initialState)
- .handleEvents(receiveOutput: currentState.send)
- .eraseToAnyPublisher()
- }.eraseToAnyPublisher()
+ return eventStream
+ .subscribe(on: spin.executer)
+ .receive(on: spin.executer)
+ .scan(spin.initialState, spin.reducer)
+ .handleEvents(receiveOutput: currentState.send)
+ .eraseToAnyPublisher()
+ }.eraseToAnyPublisher()
}
- static func start(spin: Spin) -> AnyCancellable {
+ static func start(spin: ScheduledSpin) -> AnyCancellable
+ where Executer: ExecuterDefinition, Executer.Executer: Scheduler {
AnyPublisher.stream(from: spin).consume()
}
}
diff --git a/Sources/Combine/AnyScheduler.swift b/Sources/Combine/AnyScheduler.swift
index 5318fca..e7874fc 100644
--- a/Sources/Combine/AnyScheduler.swift
+++ b/Sources/Combine/AnyScheduler.swift
@@ -6,6 +6,9 @@
//
import Combine
+import Dispatch
+import Foundation
+import SpinCommon
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public extension Scheduler {
diff --git a/Sources/Combine/DispatchQueue+Executer.swift b/Sources/Combine/DispatchQueue+Executer.swift
new file mode 100644
index 0000000..af0f83a
--- /dev/null
+++ b/Sources/Combine/DispatchQueue+Executer.swift
@@ -0,0 +1,17 @@
+//
+// DispatchQueue+Executer.swift
+//
+//
+// Created by Thibault Wittemberg on 2020-08-04.
+//
+
+import Dispatch
+import Foundation
+import SpinCommon
+
+extension DispatchQueue: ExecuterDefinition {
+ public typealias Executer = DispatchQueue
+ public static func defaultSpinExecuter() -> Executer {
+ DispatchQueue(label: "io.warpfactor.spin.dispatch-queue.\(UUID())")
+ }
+}
diff --git a/Sources/Combine/Feedback.swift b/Sources/Combine/Feedback.swift
index 8d44ec3..f0ddf22 100644
--- a/Sources/Combine/Feedback.swift
+++ b/Sources/Combine/Feedback.swift
@@ -15,6 +15,10 @@ public typealias ScheduledCombineFeedback = SpinCombine.ScheduledFeedback
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public typealias CombineFeedback = SpinCombine.Feedback
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias Feedback =
+ ScheduledFeedback
+
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public struct ScheduledFeedback: FeedbackDefinition
where SchedulerTime: Strideable, SchedulerTime.Stride: SchedulerTimeIntervalConvertible {
@@ -81,7 +85,3 @@ where SchedulerTime: Strideable, SchedulerTime.Stride: SchedulerTimeIntervalConv
self.init(effect: effect, on: executer)
}
}
-
-@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public typealias Feedback =
- ScheduledFeedback
diff --git a/Sources/Combine/OperationQueue+Executer.swift b/Sources/Combine/OperationQueue+Executer.swift
new file mode 100644
index 0000000..80d1c02
--- /dev/null
+++ b/Sources/Combine/OperationQueue+Executer.swift
@@ -0,0 +1,19 @@
+//
+// OperationQueue+Executer.swift
+//
+//
+// Created by Thibault Wittemberg on 2020-08-04.
+//
+
+import Foundation
+import SpinCommon
+
+extension OperationQueue: ExecuterDefinition {
+ public typealias Executer = OperationQueue
+ public static func defaultSpinExecuter() -> Executer {
+ let queue = OperationQueue()
+ queue.name = "io.warpfactor.spin.operationqueue.\(UUID())"
+ queue.maxConcurrentOperationCount = 1
+ return queue
+ }
+}
diff --git a/Sources/Combine/Reducer.swift b/Sources/Combine/Reducer.swift
deleted file mode 100644
index b186e3b..0000000
--- a/Sources/Combine/Reducer.swift
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// Reducer.swift
-//
-//
-// Created by Thibault Wittemberg on 2019-12-31.
-//
-
-import Combine
-import Dispatch
-import SpinCommon
-
-@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public typealias ScheduledCombineReducer = SpinCombine.ScheduledReducer
-
-@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public typealias CombineReducer = SpinCombine.Reducer
-
-@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public struct ScheduledReducer: ReducerDefinition
-where SchedulerTime: Strideable, SchedulerTime.Stride: SchedulerTimeIntervalConvertible {
- public typealias StateStream = AnyPublisher
- public typealias EventStream = AnyPublisher
- public typealias Executer = AnyScheduler
-
- public let reducer: (StateStream.Value, EventStream.Value) -> StateStream.Value
- public let executer: Executer
-
- public init(_ reducer: @escaping (StateStream.Value, EventStream.Value) -> StateStream.Value, on executer: Executer) {
- self.reducer = reducer
- self.executer = executer
- }
-
- public func scheduledReducer(with initialState: StateStream.Value) -> (EventStream) -> StateStream {
- return { events in
- events
- .receive(on: self.executer)
- .scan(initialState, self.reducer)
- .eraseToAnyPublisher()
- }
- }
-}
-
-@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public typealias Reducer
- = ScheduledReducer
-
-@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public extension ScheduledReducer
-where SchedulerTime == DispatchQueue.SchedulerTimeType, SchedulerOptions == DispatchQueue.SchedulerOptions {
- init(_ reducer: @escaping (StateStream.Value, EventStream.Value) -> StateStream.Value) {
- self.init(reducer, on: DispatchQueue.main.eraseToAnyScheduler())
- }
-}
diff --git a/Sources/Combine/RunLoop+Executer.swift b/Sources/Combine/RunLoop+Executer.swift
new file mode 100644
index 0000000..a6d3b81
--- /dev/null
+++ b/Sources/Combine/RunLoop+Executer.swift
@@ -0,0 +1,16 @@
+//
+// RunLoop+Executer.swift
+//
+//
+// Created by Thibault Wittemberg on 2020-08-04.
+//
+
+import Foundation
+import SpinCommon
+
+extension RunLoop: ExecuterDefinition {
+ public typealias Executer = RunLoop
+ public static func defaultSpinExecuter() -> Executer {
+ RunLoop.main
+ }
+}
diff --git a/Sources/Combine/Spin.swift b/Sources/Combine/Spin.swift
index 42082c9..ad7f208 100644
--- a/Sources/Combine/Spin.swift
+++ b/Sources/Combine/Spin.swift
@@ -6,10 +6,22 @@
//
import Combine
+import Dispatch
+import Foundation
import SpinCommon
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public typealias Spin = AnySpin, AnyPublisher>
+public typealias ScheduledSpin = AnySpin, AnyPublisher, Executer>
+ where Executer: ExecuterDefinition, Executer.Executer: Scheduler
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias Spin = ScheduledSpin
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias RunLoopSpin = ScheduledSpin
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias OperationQueueSpin = ScheduledSpin
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public typealias CombineSpin = SpinCombine.Spin
diff --git a/Sources/Combine/Spinner.swift b/Sources/Combine/Spinner.swift
new file mode 100644
index 0000000..6125e40
--- /dev/null
+++ b/Sources/Combine/Spinner.swift
@@ -0,0 +1,22 @@
+//
+// Spinner.swift
+//
+//
+// Created by Thibault Wittemberg on 2020-08-04.
+//
+
+import Dispatch
+import Foundation
+import SpinCommon
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias Spinner = AnySpinner
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias RunLoopSpinner = AnySpinner
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias OperationQueueSpinner = AnySpinner
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias CombineSpinner = SpinCombine.Spinner
diff --git a/Sources/Combine/SwiftUISpin.swift b/Sources/Combine/SwiftUISpin.swift
index 3ebf141..e2a3f31 100644
--- a/Sources/Combine/SwiftUISpin.swift
+++ b/Sources/Combine/SwiftUISpin.swift
@@ -13,17 +13,29 @@ import SwiftUI
public typealias CombineSwiftUISpin = SpinCombine.SwiftUISpin
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public final class SwiftUISpin: Spin, StateRenderer, EventEmitter, ObservableObject {
+public typealias SwiftUISpin = SpinCombine.ScheduledSwiftUISpin
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias RunLoopSwiftUISpin = SpinCombine.ScheduledSwiftUISpin
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias OperationQueueSwiftUISpin = SpinCombine.ScheduledSwiftUISpin
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public final class ScheduledSwiftUISpin: ScheduledSpin, StateRenderer, EventEmitter, ObservableObject
+where Executer: ExecuterDefinition, Executer.Executer: Scheduler, State: Equatable {
@Published
public var state: State
private let events = PassthroughSubject()
- private var disposeBag = [AnyCancellable]()
+ private var subscriptions = [AnyCancellable]()
- public init(spin: Spin) {
+ public init(spin: ScheduledSpin, extraRenderStateFunction: @escaping () -> Void = {}) {
self.state = spin.initialState
- super.init(initialState: spin.initialState, effects: spin.effects, scheduledReducer: spin.scheduledReducer)
+ super.init(initialState: spin.initialState, effects: spin.effects, reducer: spin.reducer, executer: spin.executer)
let uiFeedback = Feedback(uiEffects: { [weak self] state in
+ guard state != self?.state else { return }
self?.state = state
+ extraRenderStateFunction()
}, { [weak self] in
guard let `self` = self else { return Empty().eraseToAnyPublisher() }
return self.events.eraseToAnyPublisher()
@@ -32,14 +44,16 @@ public final class SwiftUISpin: Spin, StateRenderer,
}
public func emit(_ event: Event) {
- self.events.send(event)
+ self.executer.schedule { [weak self] in
+ self?.events.send(event)
+ }
}
public func start() {
- AnyPublisher.start(spin: self).disposed(by: &self.disposeBag)
+ AnyPublisher.start(spin: self).store(in: &self.subscriptions)
}
deinit {
- self.disposeBag.forEach { $0.cancel() }
+ self.subscriptions.forEach { $0.cancel() }
}
}
diff --git a/Sources/Combine/UISpin.swift b/Sources/Combine/UISpin.swift
index 7a0cedb..5ef2870 100644
--- a/Sources/Combine/UISpin.swift
+++ b/Sources/Combine/UISpin.swift
@@ -13,8 +13,18 @@ import SwiftUI
public typealias CombineUISpin = SpinCombine.UISpin
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public final class UISpin: Spin, StateRenderer, EventEmitter {
- private var disposeBag = [AnyCancellable]()
+public typealias UISpin = SpinCombine.ScheduledUISpin
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias RunLoopUISpin = SpinCombine.ScheduledUISpin
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public typealias OperationQueueUISpin = SpinCombine.ScheduledUISpin
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public final class ScheduledUISpin: ScheduledSpin, StateRenderer, EventEmitter
+where Executer: ExecuterDefinition, Executer.Executer: Scheduler {
+ private var subscriptions = [AnyCancellable]()
private let events = PassthroughSubject()
private var externalRenderFunction: ((State) -> Void)?
public var state: State {
@@ -23,31 +33,34 @@ public final class UISpin: Spin, StateRenderer, Even
}
}
- public init(spin: Spin) {
+ public init(spin: ScheduledSpin) {
self.state = spin.initialState
- super.init(initialState: spin.initialState, effects: spin.effects, scheduledReducer: spin.scheduledReducer)
+ super.init(initialState: spin.initialState, effects: spin.effects, reducer: spin.reducer, executer: spin.executer)
let uiFeedback = Feedback(uiEffects: { [weak self] state in
self?.state = state
}, { [weak self] in
guard let `self` = self else { return Empty().eraseToAnyPublisher() }
return self.events.eraseToAnyPublisher()
}, on: DispatchQueue.main.eraseToAnyScheduler())
+
self.effects = [uiFeedback.effect] + spin.effects
}
-
+
public func render(on container: Container, using function: @escaping (Container) -> (State) -> Void) {
self.externalRenderFunction = weakify(container: container, function: function)
}
-
+
public func emit(_ event: Event) {
- self.events.send(event)
+ self.executer.schedule { [weak self] in
+ self?.events.send(event)
+ }
}
-
+
public func start() {
- AnyPublisher.start(spin: self).disposed(by: &self.disposeBag)
+ AnyPublisher.start(spin: self).store(in: &self.subscriptions)
}
-
+
deinit {
- self.disposeBag.forEach { $0.cancel() }
+ self.subscriptions.forEach { $0.cancel() }
}
}
diff --git a/Sources/Common/AnySpin.swift b/Sources/Common/AnySpin.swift
index d863ccb..1e52e46 100644
--- a/Sources/Common/AnySpin.swift
+++ b/Sources/Common/AnySpin.swift
@@ -4,78 +4,74 @@
//
// Created by Thibault Wittemberg on 2019-12-29.
//
+import Foundation
-open class AnySpin: SpinDefinition {
- public var initialState: StateStream.Value
+open class AnySpin: SpinDefinition {
+ public let initialState: StateStream.Value
public var effects: [(StateStream) -> EventStream]
- public var scheduledReducer: (EventStream) -> StateStream
+ public let reducer: (StateStream.Value, EventStream.Value) -> StateStream.Value
+ public let executer: Executer.Executer
public init(initialState: StateStream.Value,
effects: [(StateStream) -> EventStream],
- scheduledReducer: @escaping (EventStream) -> StateStream) {
+ reducer: @escaping (StateStream.Value, EventStream.Value) -> StateStream.Value,
+ executer: Executer.Executer = Executer.defaultSpinExecuter()) {
self.initialState = initialState
self.effects = effects
- self.scheduledReducer = scheduledReducer
+ self.reducer = reducer
+ self.executer = executer
}
- public convenience init(initialState: StateStream.Value,
- feedback: FeedbackType,
- reducer: ReducerType)
- where
- FeedbackType: FeedbackDefinition,
- ReducerType: ReducerDefinition,
- FeedbackType.StateStream == StateStream,
- FeedbackType.EventStream == EventStream,
- FeedbackType.StateStream.Value == StateStream.Value,
- FeedbackType.StateStream == ReducerType.StateStream,
- FeedbackType.EventStream == ReducerType.EventStream {
+ public convenience init(
+ initialState: StateStream.Value,
+ feedback: Feedback,
+ reducer: Reducer,
+ executeOn executer: Executer.Executer = Executer.defaultSpinExecuter()
+ ) where
+ Feedback: FeedbackDefinition,
+ Feedback.StateStream == StateStream,
+ Feedback.EventStream == EventStream {
let effects = [feedback.effect]
- self.init(initialState: initialState, effects: effects, scheduledReducer: reducer.scheduledReducer(with: initialState))
+ self.init(initialState: initialState, effects: effects, reducer: reducer.reducer, executer: executer)
}
- public convenience init(initialState: StateStream.Value,
- @FeedbackBuilder builder: () -> (FeedbackType, ReducerType))
- where
- FeedbackType: FeedbackDefinition,
- ReducerType: ReducerDefinition,
- FeedbackType.StateStream == ReducerType.StateStream,
- FeedbackType.EventStream == ReducerType.EventStream,
- FeedbackType.StateStream == StateStream,
- FeedbackType.EventStream == EventStream {
+ public convenience init(
+ initialState: StateStream.Value,
+ executeOn executer: Executer.Executer = Executer.defaultSpinExecuter(),
+ @FeedbackBuilder builder: () -> (Feedback, Reducer)
+ ) where
+ Feedback: FeedbackDefinition,
+ Feedback.StateStream == StateStream,
+ Feedback.EventStream == EventStream{
let (feedback, reducer) = builder()
let effects = [feedback.effect]
- self.init(initialState: initialState, effects: effects, scheduledReducer: reducer.scheduledReducer(with: initialState))
+ self.init(initialState: initialState, effects: effects, reducer: reducer.reducer, executer: executer)
}
- public convenience init(initialState: StateStream.Value,
- @FeedbackBuilder builder: () -> (FeedbackA, FeedbackB, ReducerType))
- where
+ public convenience init(
+ initialState: StateStream.Value,
+ executeOn executer: Executer.Executer = Executer.defaultSpinExecuter(),
+ @FeedbackBuilder builder: () -> (FeedbackA, FeedbackB, Reducer)
+ ) where
FeedbackA: FeedbackDefinition,
FeedbackB: FeedbackDefinition,
- ReducerType: ReducerDefinition,
- FeedbackA.StateStream == ReducerType.StateStream,
- FeedbackA.EventStream == ReducerType.EventStream,
- FeedbackA.StateStream == FeedbackB.StateStream,
- FeedbackA.EventStream == FeedbackB.EventStream,
FeedbackA.StateStream == StateStream,
- FeedbackA.EventStream == EventStream {
+ FeedbackA.EventStream == EventStream,
+ FeedbackA.StateStream == FeedbackB.StateStream,
+ FeedbackA.EventStream == FeedbackB.EventStream {
let (feedback1, feedback2, reducer) = builder()
let effects = [feedback1.effect, feedback2.effect]
- self.init(initialState: initialState, effects: effects, scheduledReducer: reducer.scheduledReducer(with: initialState))
+ self.init(initialState: initialState, effects: effects, reducer: reducer.reducer, executer: executer)
}
- public convenience init(initialState: StateStream.Value,
- @FeedbackBuilder builder: () -> ( FeedbackA,
- FeedbackB,
- FeedbackC,
- ReducerType))
- where
+ public convenience init(
+ initialState: StateStream.Value,
+ executeOn executer: Executer.Executer = Executer.defaultSpinExecuter(),
+ @FeedbackBuilder builder: () -> (FeedbackA, FeedbackB, FeedbackC, Reducer)
+ ) where
FeedbackA: FeedbackDefinition,
FeedbackB: FeedbackDefinition,
FeedbackC: FeedbackDefinition,
- ReducerType: ReducerDefinition,
- FeedbackA.StateStream == ReducerType.StateStream,
- FeedbackA.EventStream == ReducerType.EventStream,
FeedbackA.StateStream == FeedbackB.StateStream,
FeedbackA.EventStream == FeedbackB.EventStream,
FeedbackB.StateStream == FeedbackC.StateStream,
@@ -84,23 +80,18 @@ open class AnySpin: Sp
FeedbackA.EventStream == EventStream {
let (feedback1, feedback2, feedback3, reducer) = builder()
let effects = [feedback1.effect, feedback2.effect, feedback3.effect]
- self.init(initialState: initialState, effects: effects, scheduledReducer: reducer.scheduledReducer(with: initialState))
+ self.init(initialState: initialState, effects: effects, reducer: reducer.reducer, executer: executer)
}
- public convenience init(initialState: StateStream.Value,
- @FeedbackBuilder builder: () -> ( FeedbackA,
- FeedbackB,
- FeedbackC,
- FeedbackD,
- ReducerType))
- where
+ public convenience init(
+ initialState: StateStream.Value,
+ executeOn executer: Executer.Executer = Executer.defaultSpinExecuter(),
+ @FeedbackBuilder builder: () -> (FeedbackA, FeedbackB, FeedbackC, FeedbackD, Reducer)
+ ) where
FeedbackA: FeedbackDefinition,
FeedbackB: FeedbackDefinition,
FeedbackC: FeedbackDefinition,
FeedbackD: FeedbackDefinition,
- ReducerType: ReducerDefinition,
- FeedbackA.StateStream == ReducerType.StateStream,
- FeedbackA.EventStream == ReducerType.EventStream,
FeedbackA.StateStream == FeedbackB.StateStream,
FeedbackA.EventStream == FeedbackB.EventStream,
FeedbackB.StateStream == FeedbackC.StateStream,
@@ -111,25 +102,19 @@ open class AnySpin: Sp
FeedbackA.EventStream == EventStream {
let (feedback1, feedback2, feedback3, feedback4, reducer) = builder()
let effects = [feedback1.effect, feedback2.effect, feedback3.effect, feedback4.effect]
- self.init(initialState: initialState, effects: effects, scheduledReducer: reducer.scheduledReducer(with: initialState))
+ self.init(initialState: initialState, effects: effects, reducer: reducer.reducer, executer: executer)
}
- public convenience init(initialState: StateStream.Value,
- @FeedbackBuilder builder: () -> ( FeedbackA,
- FeedbackB,
- FeedbackC,
- FeedbackD,
- FeedbackE,
- ReducerType))
- where
+ public convenience init(
+ initialState: StateStream.Value,
+ executeOn executer: Executer.Executer = Executer.defaultSpinExecuter(),
+ @FeedbackBuilder builder: () -> (FeedbackA, FeedbackB, FeedbackC, FeedbackD, FeedbackE, Reducer)
+ ) where
FeedbackA: FeedbackDefinition,
FeedbackB: FeedbackDefinition,
FeedbackC: FeedbackDefinition,
FeedbackD: FeedbackDefinition,
FeedbackE: FeedbackDefinition,
- ReducerType: ReducerDefinition,
- FeedbackA.StateStream == ReducerType.StateStream,
- FeedbackA.EventStream == ReducerType.EventStream,
FeedbackA.StateStream == FeedbackB.StateStream,
FeedbackA.EventStream == FeedbackB.EventStream,
FeedbackB.StateStream == FeedbackC.StateStream,
@@ -142,93 +127,86 @@ open class AnySpin: Sp
FeedbackA.EventStream == EventStream {
let (feedback1, feedback2, feedback3, feedback4, feedback5, reducer) = builder()
let effects = [feedback1.effect, feedback2.effect, feedback3.effect, feedback4.effect, feedback5.effect]
- self.init(initialState: initialState, effects: effects, scheduledReducer: reducer.scheduledReducer(with: initialState))
+ self.init(initialState: initialState, effects: effects, reducer: reducer.reducer, executer: executer)
}
}
@_functionBuilder
public struct FeedbackBuilder {
- public static func buildBlock(_ feedback: FeedbackType, _ reducer: ReducerType)
- -> (FeedbackType, ReducerType)
+ public static func buildBlock(
+ _ feedback: Feedback,
+ _ reducer: Reducer
+ ) -> (Feedback, Reducer)
where
- FeedbackType: FeedbackDefinition,
- ReducerType: ReducerDefinition,
- FeedbackType.StateStream == ReducerType.StateStream,
- FeedbackType.EventStream == ReducerType.EventStream {
+ Feedback: FeedbackDefinition {
return (feedback, reducer)
}
- public static func buildBlock(_ feedbackA: FeedbackA,
- _ feedbackB: FeedbackB,
- _ reducer: ReducerType)
- -> (FeedbackA, FeedbackB, ReducerType)
+ public static func buildBlock(
+ _ feedbackA: FeedbackA,
+ _ feedbackB: FeedbackB,
+ _ reducer: Reducer
+ ) -> (FeedbackA, FeedbackB, Reducer)
where
FeedbackA: FeedbackDefinition,
FeedbackB: FeedbackDefinition,
- ReducerType: ReducerDefinition,
FeedbackA.StateStream == FeedbackB.StateStream,
- FeedbackA.EventStream == FeedbackB.EventStream,
- FeedbackA.StateStream == ReducerType.StateStream,
- FeedbackA.EventStream == ReducerType.EventStream {
+ FeedbackA.EventStream == FeedbackB.EventStream {
return (feedbackA, feedbackB, reducer)
}
- public static func buildBlock(_ feedbackA: FeedbackA,
- _ feedbackB: FeedbackB,
- _ feedbackC: FeedbackC,
- _ reducer: ReducerType)
- -> (FeedbackA, FeedbackB, FeedbackC, ReducerType)
+ public static func buildBlock(
+ _ feedbackA: FeedbackA,
+ _ feedbackB: FeedbackB,
+ _ feedbackC: FeedbackC,
+ _ reducer: Reducer
+ ) -> (FeedbackA, FeedbackB, FeedbackC, Reducer)
where
FeedbackA: FeedbackDefinition,
FeedbackB: FeedbackDefinition,
FeedbackC: FeedbackDefinition,
- ReducerType: ReducerDefinition,
FeedbackA.StateStream == FeedbackB.StateStream,
FeedbackA.EventStream == FeedbackB.EventStream,
FeedbackB.StateStream == FeedbackC.StateStream,
- FeedbackB.EventStream == FeedbackC.EventStream,
- FeedbackA.StateStream == ReducerType.StateStream,
- FeedbackA.EventStream == ReducerType.EventStream {
+ FeedbackB.EventStream == FeedbackC.EventStream {
return (feedbackA, feedbackB, feedbackC, reducer)
}
- public static func buildBlock(_ feedbackA: FeedbackA,
- _ feedbackB: FeedbackB,
- _ feedbackC: FeedbackC,
- _ feedbackD: FeedbackD,
- _ reducer: ReducerType)
- -> (FeedbackA, FeedbackB, FeedbackC, FeedbackD, ReducerType)
+ public static func buildBlock(
+ _ feedbackA: FeedbackA,
+ _ feedbackB: FeedbackB,
+ _ feedbackC: FeedbackC,
+ _ feedbackD: FeedbackD,
+ _ reducer: Reducer
+ ) -> (FeedbackA, FeedbackB, FeedbackC, FeedbackD, Reducer)
where
FeedbackA: FeedbackDefinition,
FeedbackB: FeedbackDefinition,
FeedbackC: FeedbackDefinition,
FeedbackD: FeedbackDefinition,
- ReducerType: ReducerDefinition,
FeedbackA.StateStream == FeedbackB.StateStream,
FeedbackA.EventStream == FeedbackB.EventStream,
FeedbackB.StateStream == FeedbackC.StateStream,
FeedbackB.EventStream == FeedbackC.EventStream,
FeedbackC.StateStream == FeedbackD.StateStream,
- FeedbackC.EventStream == FeedbackD.EventStream,
- FeedbackA.StateStream == ReducerType.StateStream,
- FeedbackA.EventStream == ReducerType.EventStream {
+ FeedbackC.EventStream == FeedbackD.EventStream {
return (feedbackA, feedbackB, feedbackC, feedbackD, reducer)
}
- public static func buildBlock(_ feedbackA: FeedbackA,
- _ feedbackB: FeedbackB,
- _ feedbackC: FeedbackC,
- _ feedbackD: FeedbackD,
- _ feedbackE: FeedbackE,
- _ reducer: ReducerType)
- -> (FeedbackA, FeedbackB, FeedbackC, FeedbackD, FeedbackE, ReducerType)
+ public static func buildBlock(
+ _ feedbackA: FeedbackA,
+ _ feedbackB: FeedbackB,
+ _ feedbackC: FeedbackC,
+ _ feedbackD: FeedbackD,
+ _ feedbackE: FeedbackE,
+ _ reducer: Reducer
+ ) -> (FeedbackA, FeedbackB, FeedbackC, FeedbackD, FeedbackE, Reducer)
where
FeedbackA: FeedbackDefinition,
FeedbackB: FeedbackDefinition,
FeedbackC: FeedbackDefinition,
FeedbackD: FeedbackDefinition,
FeedbackE: FeedbackDefinition,
- ReducerType: ReducerDefinition,
FeedbackA.StateStream == FeedbackB.StateStream,
FeedbackA.EventStream == FeedbackB.EventStream,
FeedbackB.StateStream == FeedbackC.StateStream,
@@ -236,9 +214,7 @@ public struct FeedbackBuilder {
FeedbackC.StateStream == FeedbackD.StateStream,
FeedbackC.EventStream == FeedbackD.EventStream,
FeedbackD.StateStream == FeedbackE.StateStream,
- FeedbackD.EventStream == FeedbackE.EventStream,
- FeedbackA.StateStream == ReducerType.StateStream,
- FeedbackA.EventStream == ReducerType.EventStream {
+ FeedbackD.EventStream == FeedbackE.EventStream {
return (feedbackA, feedbackB, feedbackC, feedbackD, feedbackE, reducer)
}
}
diff --git a/Sources/Common/AnySpinner.swift b/Sources/Common/AnySpinner.swift
new file mode 100644
index 0000000..eda1ec6
--- /dev/null
+++ b/Sources/Common/AnySpinner.swift
@@ -0,0 +1,64 @@
+//
+// Spinner.swift
+//
+//
+// Created by Thibault Wittemberg on 2019-12-29.
+//
+
+import Foundation
+
+public class AnySpinner {
+ let initialState: State
+ let executer: Executer.Executer
+
+ init (initialState state: State, executer: Executer.Executer) {
+ self.initialState = state
+ self.executer = executer
+ }
+
+ public static func initialState(_ state: State, executeOn executer: Executer.Executer = Executer.defaultSpinExecuter()) -> AnySpinner {
+ return AnySpinner(initialState: state, executer: executer)
+ }
+
+ public func feedback(_ feedback: Feedback) -> SpinnerFeedback
+ where
+ Feedback: FeedbackDefinition,
+ Feedback.StateStream.Value == State {
+ return SpinnerFeedback(initialState: self.initialState,
+ feedbacks: [feedback],
+ executer: self.executer)
+ }
+}
+
+public class SpinnerFeedback {
+ let initialState: StateStream.Value
+ var effects: [(StateStream) -> EventStream]
+ let executer: Executer.Executer
+
+ init (initialState state: StateStream.Value,
+ feedbacks: [Feedback],
+ executer: Executer.Executer)
+ where
+ Feedback.StateStream == StateStream,
+ Feedback.EventStream == EventStream {
+ self.initialState = state
+ self.effects = feedbacks.map { $0.effect }
+ self.executer = executer
+ }
+
+ public func feedback(_ feedback: Feedback) -> SpinnerFeedback
+ where
+ Feedback: FeedbackDefinition,
+ Feedback.StateStream == StateStream,
+ Feedback.EventStream == EventStream {
+ self.effects.append(feedback.effect)
+ return self
+ }
+
+ public func reducer(_ reducer: Reducer) -> AnySpin {
+ return AnySpin(initialState: self.initialState,
+ effects: self.effects,
+ reducer: reducer.reducer,
+ executer: self.executer)
+ }
+}
diff --git a/Sources/Common/ExecuterDefinition.swift b/Sources/Common/ExecuterDefinition.swift
new file mode 100644
index 0000000..a8508ec
--- /dev/null
+++ b/Sources/Common/ExecuterDefinition.swift
@@ -0,0 +1,11 @@
+//
+// ExecuterDefinition.swift
+//
+//
+// Created by Thibault Wittemberg on 2020-08-04.
+//
+
+public protocol ExecuterDefinition {
+ associatedtype Executer
+ static func defaultSpinExecuter() -> Executer
+}
diff --git a/Sources/Common/Reducer.swift b/Sources/Common/Reducer.swift
new file mode 100644
index 0000000..d5ad878
--- /dev/null
+++ b/Sources/Common/Reducer.swift
@@ -0,0 +1,17 @@
+//
+// Reducer.swift
+//
+//
+// Created by Thibault Wittemberg on 2019-12-29.
+//
+
+/// A Reducer represents the way a reactive stream of `Event` can
+/// sequentially mutate an initial `State` over time be executing a sequence of `Feedbacks`
+
+public struct Reducer {
+ public let reducer: (State, Event) -> State
+
+ public init(_ reducer: @escaping (State, Event) -> State) {
+ self.reducer = reducer
+ }
+}
diff --git a/Sources/Common/ReducerDefinition.swift b/Sources/Common/ReducerDefinition.swift
deleted file mode 100644
index 07c9a3f..0000000
--- a/Sources/Common/ReducerDefinition.swift
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// ReducerDefinition.swift
-//
-//
-// Created by Thibault Wittemberg on 2019-12-29.
-//
-
-/// A Reducer represents the way a reactive stream of `Event` can
-/// sequentially mutate an initial `State` over time be executing a sequence of `Feedbacks`
-public protocol ReducerDefinition {
- associatedtype StateStream: ReactiveStream
- associatedtype EventStream: ReactiveStream
- associatedtype Executer
-
- var reducer: (StateStream.Value, EventStream.Value) -> StateStream.Value { get }
- var executer: Executer { get }
-
- init(_ reducer: @escaping (StateStream.Value, EventStream.Value) -> StateStream.Value, on executer: Executer)
- func scheduledReducer(with initialState: StateStream.Value) -> (EventStream) -> StateStream
-}
diff --git a/Sources/Common/SpinDefinition.swift b/Sources/Common/SpinDefinition.swift
index abd7103..bda7ada 100644
--- a/Sources/Common/SpinDefinition.swift
+++ b/Sources/Common/SpinDefinition.swift
@@ -11,8 +11,10 @@
public protocol SpinDefinition {
associatedtype StateStream: ReactiveStream
associatedtype EventStream: ReactiveStream
+ associatedtype Executer
var initialState: StateStream.Value { get }
var effects: [(StateStream) -> EventStream] { get }
- var scheduledReducer: (EventStream) -> StateStream { get }
+ var reducer: (StateStream.Value, EventStream.Value) -> StateStream.Value { get }
+ var executer: Executer { get }
}
diff --git a/Sources/Common/Spinner.swift b/Sources/Common/Spinner.swift
deleted file mode 100644
index c756ffb..0000000
--- a/Sources/Common/Spinner.swift
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-// Spinner.swift
-//
-//
-// Created by Thibault Wittemberg on 2019-12-29.
-//
-
-public class Spinner {
- internal let initialState: State
-
- internal init (initialState state: State) {
- self.initialState = state
- }
-
- public static func initialState(_ state: State) -> Spinner {
- return Spinner(initialState: state)
- }
-
- public func feedback(_ feedback: FeedbackType) -> SpinnerFeedback< FeedbackType.StateStream,
- FeedbackType.EventStream>
- where FeedbackType.StateStream.Value == State {
- return SpinnerFeedback< FeedbackType.StateStream, FeedbackType.EventStream>(initialState: self.initialState,
- feedbacks: [feedback])
- }
-}
-
-public class SpinnerFeedback {
- internal let initialState: StateStream.Value
- internal var effects: [(StateStream) -> EventStream]
-
- internal init (initialState state: StateStream.Value,
- feedbacks: [FeedbackType])
- where
- FeedbackType.StateStream == StateStream,
- FeedbackType.EventStream == EventStream {
- self.initialState = state
- self.effects = feedbacks.map { $0.effect }
- }
-
- public func feedback(_ feedback: NewFeedbackType) -> SpinnerFeedback
- where
- NewFeedbackType: FeedbackDefinition,
- NewFeedbackType.StateStream == StateStream,
- NewFeedbackType.EventStream == EventStream {
- self.effects.append(feedback.effect)
- return self
- }
-
- public func reducer(_ reducer: ReducerType) -> AnySpin
- where
- ReducerType: ReducerDefinition,
- ReducerType.StateStream == StateStream,
- ReducerType.EventStream == EventStream {
- return AnySpin(initialState: self.initialState,
- effects: self.effects,
- scheduledReducer: reducer.scheduledReducer(with: initialState))
- }
-}
diff --git a/Sources/ReactiveSwift/Disposable+DisposeBag.swift b/Sources/ReactiveSwift/Disposable+add.swift
similarity index 63%
rename from Sources/ReactiveSwift/Disposable+DisposeBag.swift
rename to Sources/ReactiveSwift/Disposable+add.swift
index 84d7ed2..b5abc53 100644
--- a/Sources/ReactiveSwift/Disposable+DisposeBag.swift
+++ b/Sources/ReactiveSwift/Disposable+add.swift
@@ -1,5 +1,5 @@
//
-// Disposable+DisposeBag.swift
+// Disposable+add.swift
//
//
// Created by Thibault Wittemberg on 2019-12-31.
@@ -8,7 +8,7 @@
import ReactiveSwift
public extension Disposable {
- func disposed(by disposable: CompositeDisposable) {
+ func add(to disposable: CompositeDisposable) {
disposable.add(self)
}
}
diff --git a/Sources/ReactiveSwift/Executer.swift b/Sources/ReactiveSwift/Executer.swift
new file mode 100644
index 0000000..d427bec
--- /dev/null
+++ b/Sources/ReactiveSwift/Executer.swift
@@ -0,0 +1,17 @@
+//
+// Executer.swift
+//
+//
+// Created by Thibault Wittemberg on 2020-08-05.
+//
+
+import Foundation
+import ReactiveSwift
+import SpinCommon
+
+public class Executer: ExecuterDefinition {
+ public typealias Executer = Scheduler
+ public static func defaultSpinExecuter() -> Executer {
+ QueueScheduler(qos: .default, name: "io.warpfactor.spin.dispatch-queue.\(UUID())")
+ }
+}
diff --git a/Sources/ReactiveSwift/Reducer.swift b/Sources/ReactiveSwift/Reducer.swift
deleted file mode 100644
index f8fbded..0000000
--- a/Sources/ReactiveSwift/Reducer.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-// Reducer.swift
-//
-//
-// Created by Thibault Wittemberg on 2019-12-31.
-//
-
-import ReactiveSwift
-import SpinCommon
-
-public typealias ReactiveReducer = SpinReactiveSwift.Reducer
-
-public struct Reducer: ReducerDefinition {
- public typealias StateStream = SignalProducer
- public typealias EventStream = SignalProducer
- public typealias Executer = Scheduler
-
- public let reducer: (StateStream.Value, EventStream.Value) -> StateStream.Value
- public let executer: Executer
-
- public init(_ reducer: @escaping (StateStream.Value, EventStream.Value) -> StateStream.Value,
- on executer: Executer = QueueScheduler.main) {
- self.reducer = reducer
- self.executer = executer
- }
-
- public func scheduledReducer(with initialState: StateStream.Value) -> (EventStream) -> StateStream {
- return { events in
- events
- .observe(on: self.executer)
- .scan(initialState, self.reducer)
- }
- }
-}
diff --git a/Sources/ReactiveSwift/SignalProducer+streamFromSpin.swift b/Sources/ReactiveSwift/SignalProducer+streamFromSpin.swift
index e1dddbe..3cecf3d 100644
--- a/Sources/ReactiveSwift/SignalProducer+streamFromSpin.swift
+++ b/Sources/ReactiveSwift/SignalProducer+streamFromSpin.swift
@@ -15,16 +15,19 @@ public extension SignalProducer where Error == Never {
guard let spin = spin else { return .empty }
- let currentState = MutableProperty(spin.initialState)
+ let (signal, currentState) = Signal.pipe()
// merging all the effects into one event stream
- let eventStreams = spin.effects.map { $0(currentState.producer) }
+ let stateInputStream = signal.producer
+ let eventStreams = spin.effects.map { $0(stateInputStream) }
let eventStream = SignalProducer.merge(eventStreams)
-
- return spin
- .scheduledReducer(eventStream)
+
+ return eventStream
+ .observe(on: spin.executer)
+ .scan(spin.initialState, spin.reducer)
.prefix(value: spin.initialState)
- .on(value: { currentState.swap($0) })
+ .on(started: { currentState.send(value: spin.initialState) })
+ .on(value: { currentState.send(value: $0) })
}
}
diff --git a/Sources/ReactiveSwift/Spin.swift b/Sources/ReactiveSwift/Spin.swift
index 31f8563..be31394 100644
--- a/Sources/ReactiveSwift/Spin.swift
+++ b/Sources/ReactiveSwift/Spin.swift
@@ -8,6 +8,6 @@
import ReactiveSwift
import SpinCommon
-public typealias Spin = AnySpin, SignalProducer>
+public typealias Spin = AnySpin, SignalProducer, Executer>
public typealias ReactiveSpin = SpinReactiveSwift.Spin
diff --git a/Sources/ReactiveSwift/Spinner.swift b/Sources/ReactiveSwift/Spinner.swift
new file mode 100644
index 0000000..200f02e
--- /dev/null
+++ b/Sources/ReactiveSwift/Spinner.swift
@@ -0,0 +1,12 @@
+//
+// Spinner.swift
+//
+//
+// Created by Thibault Wittemberg on 2020-08-05.
+//
+
+import SpinCommon
+
+public typealias Spinner = AnySpinner
+
+public typealias ReactiveSpinner = SpinReactiveSwift.Spinner
diff --git a/Sources/ReactiveSwift/SwiftUISpin.swift b/Sources/ReactiveSwift/SwiftUISpin.swift
index ac28e7e..5dea76d 100644
--- a/Sources/ReactiveSwift/SwiftUISpin.swift
+++ b/Sources/ReactiveSwift/SwiftUISpin.swift
@@ -14,17 +14,20 @@ import SwiftUI
public typealias ReactiveSwiftUISpin = SpinReactiveSwift.SwiftUISpin
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public final class SwiftUISpin: Spin, StateRenderer, EventEmitter, ObservableObject {
+public final class SwiftUISpin: Spin, StateRenderer, EventEmitter, ObservableObject
+where State: Equatable {
@Published
public var state: State
private let (eventsProducer, eventsObserver) = Signal.pipe()
- private let disposeBag = CompositeDisposable()
+ private let subscriptions = CompositeDisposable()
- public init(spin: Spin) {
+ public init(spin: Spin, extraRenderStateFunction: @escaping () -> Void = {}) {
self.state = spin.initialState
- super.init(initialState: spin.initialState, effects: spin.effects, scheduledReducer: spin.scheduledReducer)
+ super.init(initialState: spin.initialState, effects: spin.effects, reducer: spin.reducer, executer: spin.executer)
let uiFeedback = Feedback(uiEffects: { [weak self] state in
+ guard state != self?.state else { return }
self?.state = state
+ extraRenderStateFunction()
}, { [weak self] in
guard let `self` = self else { return .empty }
return self.eventsProducer.producer
@@ -33,15 +36,17 @@ public final class SwiftUISpin: Spin, StateRenderer,
}
public func emit(_ event: Event) {
- self.eventsObserver.send(value: event)
+ self.executer.schedule { [weak self] in
+ self?.eventsObserver.send(value: event)
+ }
}
public func start() {
- SignalProducer.start(spin: self).disposed(by: self.disposeBag)
+ SignalProducer.start(spin: self).add(to: self.subscriptions)
}
deinit {
- self.disposeBag.dispose()
+ self.subscriptions.dispose()
}
}
#endif
diff --git a/Sources/ReactiveSwift/UISpin.swift b/Sources/ReactiveSwift/UISpin.swift
index 0e050f2..45034d7 100644
--- a/Sources/ReactiveSwift/UISpin.swift
+++ b/Sources/ReactiveSwift/UISpin.swift
@@ -11,7 +11,7 @@ import SpinCommon
public typealias ReactiveUISpin = SpinReactiveSwift.UISpin
public final class UISpin: Spin, StateRenderer, EventEmitter {
- private let disposeBag = CompositeDisposable()
+ private let subscriptions = CompositeDisposable()
private let (eventsProducer, eventsObserver) = Signal.pipe()
private var externalRenderFunction: ((State) -> Void)?
public var state: State {
@@ -22,7 +22,7 @@ public final class UISpin: Spin, StateRenderer, Even
public init(spin: Spin) {
self.state = spin.initialState
- super.init(initialState: spin.initialState, effects: spin.effects, scheduledReducer: spin.scheduledReducer)
+ super.init(initialState: spin.initialState, effects: spin.effects, reducer: spin.reducer, executer: spin.executer)
let uiFeedback = Feedback(uiEffects: { [weak self] state in
self?.state = state
}, { [weak self] in
@@ -37,14 +37,16 @@ public final class UISpin: Spin, StateRenderer, Even
}
public func emit(_ event: Event) {
- self.eventsObserver.send(value: event)
+ self.executer.schedule { [weak self] in
+ self?.eventsObserver.send(value: event)
+ }
}
public func start() {
- SignalProducer.start(spin: self).disposed(by: self.disposeBag)
+ SignalProducer.start(spin: self).add(to: self.subscriptions)
}
deinit {
- self.disposeBag.dispose()
+ self.subscriptions.dispose()
}
}
diff --git a/Sources/RxSwift/Executer.swift b/Sources/RxSwift/Executer.swift
new file mode 100644
index 0000000..fce100e
--- /dev/null
+++ b/Sources/RxSwift/Executer.swift
@@ -0,0 +1,17 @@
+//
+// Executer.swift
+//
+//
+// Created by Thibault Wittemberg on 2020-08-05.
+//
+
+import Foundation
+import RxSwift
+import SpinCommon
+
+public class Executer: ExecuterDefinition {
+ public typealias Executer = ImmediateSchedulerType
+ public static func defaultSpinExecuter() -> Executer {
+ SerialDispatchQueueScheduler(internalSerialQueueName: "io.warpfactor.spin.dispatch-queue.\(UUID())")
+ }
+}
diff --git a/Sources/RxSwift/Gear.swift b/Sources/RxSwift/Gear.swift
index e200587..4842c32 100644
--- a/Sources/RxSwift/Gear.swift
+++ b/Sources/RxSwift/Gear.swift
@@ -5,11 +5,11 @@
// Created by Thibault Wittemberg on 2020-07-26.
//
-import RxSwift
import RxRelay
+import RxSwift
import SpinCommon
-public typealias CombineGear = SpinRxSwift.Gear
+public typealias RxGear = SpinRxSwift.Gear
open class Gear: GearDefinition {
@@ -17,11 +17,11 @@ open class Gear: GearDefinition {
self.eventSubject.asObservable()
}
- let eventSubject = PublishSubject()
+ let eventSubject = PublishRelay()
public init() {}
open func propagate(event: Event) {
- self.eventSubject.onNext(event)
+ self.eventSubject.accept(event)
}
}
diff --git a/Sources/RxSwift/Observable+ReactiveStream.swift b/Sources/RxSwift/Observable+ReactiveStream.swift
index 05b7041..a7448a7 100644
--- a/Sources/RxSwift/Observable+ReactiveStream.swift
+++ b/Sources/RxSwift/Observable+ReactiveStream.swift
@@ -14,7 +14,7 @@ extension Observable: ReactiveStream {
public static func emptyStream() -> Self {
guard let emptyStream = Observable.empty() as? Self else {
- fatalError("Observable cannot be subclassed to be able to use the framework")
+ fatalError("Observable cannot be subclassed to be able to get an emptyStream()")
}
return emptyStream
diff --git a/Sources/RxSwift/Observable+streamFromSpin.swift b/Sources/RxSwift/Observable+streamFromSpin.swift
index 632537b..0f6af8a 100644
--- a/Sources/RxSwift/Observable+streamFromSpin.swift
+++ b/Sources/RxSwift/Observable+streamFromSpin.swift
@@ -5,6 +5,7 @@
// Created by Thibault Wittemberg on 2020-02-07.
//
+import RxRelay
import RxSwift
import SpinCommon
@@ -14,16 +15,18 @@ public extension Observable {
guard let spin = spin else { return .empty() }
- let currentState = ReplaySubject.create(bufferSize: 1)
+ let currentState = BehaviorRelay(value: spin.initialState)
// merging all the effects into one event stream
- let eventStreams = spin.effects.map { $0(currentState.asObservable()) }
+ let stateInputStream = currentState.asObservable()
+ let eventStreams = spin.effects.map { $0(stateInputStream) }
let eventStream = Observable.merge(eventStreams).catchError { _ in return .empty() }
- return spin
- .scheduledReducer(eventStream)
- .startWith(spin.initialState)
- .do(onNext: { currentState.onNext($0) })
+ return eventStream
+ .subscribeOn(spin.executer)
+ .observeOn(spin.executer)
+ .scan(spin.initialState, accumulator: spin.reducer)
+ .do(onNext: { currentState.accept($0) })
}
}
diff --git a/Sources/RxSwift/Reducer.swift b/Sources/RxSwift/Reducer.swift
deleted file mode 100644
index f2beb93..0000000
--- a/Sources/RxSwift/Reducer.swift
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// Reducer.swift
-//
-//
-// Created by Thibault Wittemberg on 2019-12-31.
-//
-
-import RxRelay
-import RxSwift
-import SpinCommon
-
-public typealias RxReducer = SpinRxSwift.Reducer
-
-public struct Reducer: ReducerDefinition {
- public typealias StateStream = Observable
- public typealias EventStream = Observable
- public typealias Executer = ImmediateSchedulerType
-
- public let reducer: (StateStream.Value, EventStream.Value) -> StateStream.Value
- public let executer: Executer
-
- public init(_ reducer: @escaping (StateStream.Value, EventStream.Value) -> StateStream.Value,
- on executer: Executer = CurrentThreadScheduler.instance) {
- self.reducer = reducer
- self.executer = executer
- }
-
- public func scheduledReducer(with initialState: StateStream.Value) -> (EventStream) -> StateStream {
- return { events in
- events
- .observeOn(self.executer)
- .scan(initialState, accumulator: self.reducer)
- }
- }
-}
diff --git a/Sources/RxSwift/Spin.swift b/Sources/RxSwift/Spin.swift
index f279a96..90d38e3 100644
--- a/Sources/RxSwift/Spin.swift
+++ b/Sources/RxSwift/Spin.swift
@@ -8,6 +8,6 @@
import RxSwift
import SpinCommon
-public typealias Spin = AnySpin, Observable>
+public typealias Spin = AnySpin, Observable, Executer>
public typealias RxSpin = SpinRxSwift.Spin
diff --git a/Sources/RxSwift/Spinner.swift b/Sources/RxSwift/Spinner.swift
new file mode 100644
index 0000000..1f06ae5
--- /dev/null
+++ b/Sources/RxSwift/Spinner.swift
@@ -0,0 +1,12 @@
+//
+// Spinner.swift
+//
+//
+// Created by Thibault Wittemberg on 2020-08-05.
+//
+
+import SpinCommon
+
+public typealias Spinner = AnySpinner
+
+public typealias RxSpinner = SpinRxSwift.Spinner
diff --git a/Sources/RxSwift/SwiftUISpin.swift b/Sources/RxSwift/SwiftUISpin.swift
index a15e18f..138e7e5 100644
--- a/Sources/RxSwift/SwiftUISpin.swift
+++ b/Sources/RxSwift/SwiftUISpin.swift
@@ -15,28 +15,34 @@ import SwiftUI
public typealias RxSwiftUISpin = SpinRxSwift.SwiftUISpin
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
-public final class SwiftUISpin: Spin, StateRenderer, EventEmitter, ObservableObject {
+public final class SwiftUISpin: Spin, StateRenderer, EventEmitter, ObservableObject
+where State: Equatable {
@Published
public var state: State
private let events = PublishRelay()
private let disposeBag = DisposeBag()
-
- public init(spin: Spin) {
+
+ public init(spin: Spin, extraRenderStateFunction: @escaping () -> Void = {}) {
self.state = spin.initialState
- super.init(initialState: spin.initialState, effects: spin.effects, scheduledReducer: spin.scheduledReducer)
+ super.init(initialState: spin.initialState, effects: spin.effects, reducer: spin.reducer, executer: spin.executer)
let uiFeedback = Feedback(uiEffects: { [weak self] state in
+ guard state != self?.state else { return }
self?.state = state
+ extraRenderStateFunction()
}, { [weak self] in
guard let `self` = self else { return .empty() }
return self.events.asObservable()
}, on: MainScheduler.instance)
self.effects = [uiFeedback.effect] + spin.effects
}
-
+
public func emit(_ event: Event) {
- self.events.accept(event)
+ _ = self.executer.schedule(()) { [weak self] _ -> Disposable in
+ self?.events.accept(event)
+ return Disposables.create()
+ }
}
-
+
public func start() {
Observable.start(spin: self).disposed(by: self.disposeBag)
}
diff --git a/Sources/RxSwift/UISpin.swift b/Sources/RxSwift/UISpin.swift
index 6055f00..70fa5de 100644
--- a/Sources/RxSwift/UISpin.swift
+++ b/Sources/RxSwift/UISpin.swift
@@ -23,7 +23,7 @@ public final class UISpin: Spin, StateRenderer, Even
public init(spin: Spin) {
self.state = spin.initialState
- super.init(initialState: spin.initialState, effects: spin.effects, scheduledReducer: spin.scheduledReducer)
+ super.init(initialState: spin.initialState, effects: spin.effects, reducer: spin.reducer, executer: spin.executer)
let uiFeedback = Feedback(uiEffects: { [weak self] state in
self?.state = state
}, { [weak self] in
@@ -38,7 +38,10 @@ public final class UISpin: Spin, StateRenderer, Even
}
public func emit(_ event: Event) {
- self.events.accept(event)
+ _ = self.executer.schedule(()) { [weak self] _ -> Disposable in
+ self?.events.accept(event)
+ return Disposables.create()
+ }
}
public func start() {
diff --git a/Spin.Swift.xcodeproj/project.pbxproj b/Spin.Swift.xcodeproj/project.pbxproj
index 40ec24a..6b551b6 100644
--- a/Spin.Swift.xcodeproj/project.pbxproj
+++ b/Spin.Swift.xcodeproj/project.pbxproj
@@ -8,35 +8,29 @@
/* Begin PBXBuildFile section */
742CD33D2443FC8900A9AB4C /* Spin_ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 742CD33B2443FC8900A9AB4C /* Spin_ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 742CD3412443FCBF00A9AB4C /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60442443BAC30054C286 /* Reducer.swift */; };
742CD3422443FCBF00A9AB4C /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60452443BAC30054C286 /* UISpin.swift */; };
742CD3432443FCBF00A9AB4C /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60462443BAC30054C286 /* Spin.swift */; };
742CD3442443FCBF00A9AB4C /* SignalProducer+Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60472443BAC30054C286 /* SignalProducer+Deferred.swift */; };
- 742CD3452443FCBF00A9AB4C /* Disposable+DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60482443BAC30054C286 /* Disposable+DisposeBag.swift */; };
742CD3462443FCBF00A9AB4C /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60492443BAC30054C286 /* SwiftUISpin.swift */; };
742CD3472443FCBF00A9AB4C /* SignalProducer+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604A2443BAC30054C286 /* SignalProducer+ReactiveStream.swift */; };
742CD3482443FCBF00A9AB4C /* SignalProducer+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604B2443BAC30054C286 /* SignalProducer+streamFromSpin.swift */; };
742CD3492443FCBF00A9AB4C /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604C2443BAC30054C286 /* Feedback.swift */; };
742CD3612443FD2300A9AB4C /* Spin_Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 742CD35F2443FD2300A9AB4C /* Spin_Swift.h */; settings = {ATTRIBUTES = (Public, ); }; };
742CD3652443FD7100A9AB4C /* FeedbackDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604E2443BAC30054C286 /* FeedbackDefinition.swift */; };
- 742CD3662443FD7100A9AB4C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604F2443BAC30054C286 /* Spinner.swift */; };
742CD3672443FD7100A9AB4C /* SpinDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60502443BAC30054C286 /* SpinDefinition.swift */; };
742CD3682443FD7100A9AB4C /* ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60512443BAC30054C286 /* ReactiveStream.swift */; };
742CD3692443FD7100A9AB4C /* Weakify.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60522443BAC30054C286 /* Weakify.swift */; };
742CD36A2443FD7100A9AB4C /* AnySpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60532443BAC30054C286 /* AnySpin.swift */; };
742CD36B2443FD7100A9AB4C /* StateRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60542443BAC30054C286 /* StateRenderer.swift */; };
- 742CD36C2443FD7100A9AB4C /* ReducerDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60552443BAC30054C286 /* ReducerDefinition.swift */; };
742CD36D2443FD7100A9AB4C /* FeedbackDefinition+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60562443BAC30054C286 /* FeedbackDefinition+Default.swift */; };
742CD36E2443FD7100A9AB4C /* EventEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60572443BAC30054C286 /* EventEmitter.swift */; };
742CD37E2443FE9000A9AB4C /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; };
742CD3832443FE9700A9AB4C /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; };
742CD3872443FE9D00A9AB4C /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; platformFilter = ios; };
742CD39D2443FF9E00A9AB4C /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; };
- 742CD3A22443FFA600A9AB4C /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60442443BAC30054C286 /* Reducer.swift */; };
742CD3A32443FFA600A9AB4C /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60452443BAC30054C286 /* UISpin.swift */; };
742CD3A42443FFA600A9AB4C /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60462443BAC30054C286 /* Spin.swift */; };
742CD3A52443FFA600A9AB4C /* SignalProducer+Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60472443BAC30054C286 /* SignalProducer+Deferred.swift */; };
- 742CD3A62443FFA600A9AB4C /* Disposable+DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60482443BAC30054C286 /* Disposable+DisposeBag.swift */; };
742CD3A72443FFA600A9AB4C /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60492443BAC30054C286 /* SwiftUISpin.swift */; };
742CD3A82443FFA600A9AB4C /* SignalProducer+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604A2443BAC30054C286 /* SignalProducer+ReactiveStream.swift */; };
742CD3A92443FFA600A9AB4C /* SignalProducer+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604B2443BAC30054C286 /* SignalProducer+streamFromSpin.swift */; };
@@ -47,22 +41,18 @@
742CD3AE2443FFC400A9AB4C /* Spin_RxSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 74FB609E2443BC990054C286 /* Spin_RxSwift.h */; settings = {ATTRIBUTES = (Public, ); }; };
742CD3B82444BB4300A9AB4C /* Spin_ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 742CD3B62444BB4300A9AB4C /* Spin_ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; };
742CD3C02444BB8A00A9AB4C /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; };
- 742CD3C52444BB9500A9AB4C /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60442443BAC30054C286 /* Reducer.swift */; };
742CD3C62444BB9500A9AB4C /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60452443BAC30054C286 /* UISpin.swift */; };
742CD3C72444BB9500A9AB4C /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60462443BAC30054C286 /* Spin.swift */; };
742CD3C82444BB9500A9AB4C /* SignalProducer+Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60472443BAC30054C286 /* SignalProducer+Deferred.swift */; };
- 742CD3C92444BB9500A9AB4C /* Disposable+DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60482443BAC30054C286 /* Disposable+DisposeBag.swift */; };
742CD3CA2444BB9500A9AB4C /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60492443BAC30054C286 /* SwiftUISpin.swift */; };
742CD3CB2444BB9500A9AB4C /* SignalProducer+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604A2443BAC30054C286 /* SignalProducer+ReactiveStream.swift */; };
742CD3CC2444BB9500A9AB4C /* SignalProducer+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604B2443BAC30054C286 /* SignalProducer+streamFromSpin.swift */; };
742CD3CD2444BB9500A9AB4C /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604C2443BAC30054C286 /* Feedback.swift */; };
742CD3D72444BC1D00A9AB4C /* Spin_ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 742CD3D52444BC1D00A9AB4C /* Spin_ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; };
742CD3DF2444BC3200A9AB4C /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; };
- 742CD3E42444BC5E00A9AB4C /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60442443BAC30054C286 /* Reducer.swift */; };
742CD3E52444BC5E00A9AB4C /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60452443BAC30054C286 /* UISpin.swift */; };
742CD3E62444BC5E00A9AB4C /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60462443BAC30054C286 /* Spin.swift */; };
742CD3E72444BC5E00A9AB4C /* SignalProducer+Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60472443BAC30054C286 /* SignalProducer+Deferred.swift */; };
- 742CD3E82444BC5E00A9AB4C /* Disposable+DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60482443BAC30054C286 /* Disposable+DisposeBag.swift */; };
742CD3E92444BC5E00A9AB4C /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60492443BAC30054C286 /* SwiftUISpin.swift */; };
742CD3EA2444BC5E00A9AB4C /* SignalProducer+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604A2443BAC30054C286 /* SignalProducer+ReactiveStream.swift */; };
742CD3EB2444BC5E00A9AB4C /* SignalProducer+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB604B2443BAC30054C286 /* SignalProducer+streamFromSpin.swift */; };
@@ -71,10 +61,8 @@
742CD3FA2444BC9C00A9AB4C /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; platformFilter = ios; };
742CD3FF2444BCA700A9AB4C /* AnyPublisher+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60612443BAC30054C286 /* AnyPublisher+streamFromSpin.swift */; };
742CD4002444BCA700A9AB4C /* AnyPublisher+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60622443BAC30054C286 /* AnyPublisher+ReactiveStream.swift */; };
- 742CD4012444BCA700A9AB4C /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60632443BAC30054C286 /* Reducer.swift */; };
742CD4022444BCA700A9AB4C /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60642443BAC30054C286 /* UISpin.swift */; };
742CD4032444BCA700A9AB4C /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60652443BAC30054C286 /* Spin.swift */; };
- 742CD4042444BCA700A9AB4C /* AnyCancellable+DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60662443BAC30054C286 /* AnyCancellable+DisposeBag.swift */; };
742CD4052444BCA700A9AB4C /* AnyScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60672443BAC30054C286 /* AnyScheduler.swift */; };
742CD4062444BCA700A9AB4C /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60682443BAC30054C286 /* SwiftUISpin.swift */; };
742CD4072444BCA700A9AB4C /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60692443BAC30054C286 /* Feedback.swift */; };
@@ -82,10 +70,8 @@
742CD4152444BDE900A9AB4C /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; };
742CD41A2444BE0800A9AB4C /* AnyPublisher+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60612443BAC30054C286 /* AnyPublisher+streamFromSpin.swift */; };
742CD41B2444BE0800A9AB4C /* AnyPublisher+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60622443BAC30054C286 /* AnyPublisher+ReactiveStream.swift */; };
- 742CD41C2444BE0800A9AB4C /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60632443BAC30054C286 /* Reducer.swift */; };
742CD41D2444BE0800A9AB4C /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60642443BAC30054C286 /* UISpin.swift */; };
742CD41E2444BE0800A9AB4C /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60652443BAC30054C286 /* Spin.swift */; };
- 742CD41F2444BE0800A9AB4C /* AnyCancellable+DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60662443BAC30054C286 /* AnyCancellable+DisposeBag.swift */; };
742CD4202444BE0800A9AB4C /* AnyScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60672443BAC30054C286 /* AnyScheduler.swift */; };
742CD4212444BE0800A9AB4C /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60682443BAC30054C286 /* SwiftUISpin.swift */; };
742CD4222444BE0800A9AB4C /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60692443BAC30054C286 /* Feedback.swift */; };
@@ -95,19 +81,15 @@
742CD4422444BE7100A9AB4C /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; };
742CD4472444BE9400A9AB4C /* AnyPublisher+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60612443BAC30054C286 /* AnyPublisher+streamFromSpin.swift */; };
742CD4482444BE9400A9AB4C /* AnyPublisher+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60622443BAC30054C286 /* AnyPublisher+ReactiveStream.swift */; };
- 742CD4492444BE9400A9AB4C /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60632443BAC30054C286 /* Reducer.swift */; };
742CD44A2444BE9400A9AB4C /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60642443BAC30054C286 /* UISpin.swift */; };
742CD44B2444BE9400A9AB4C /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60652443BAC30054C286 /* Spin.swift */; };
- 742CD44C2444BE9400A9AB4C /* AnyCancellable+DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60662443BAC30054C286 /* AnyCancellable+DisposeBag.swift */; };
742CD44D2444BE9400A9AB4C /* AnyScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60672443BAC30054C286 /* AnyScheduler.swift */; };
742CD44E2444BE9400A9AB4C /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60682443BAC30054C286 /* SwiftUISpin.swift */; };
742CD44F2444BE9400A9AB4C /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60692443BAC30054C286 /* Feedback.swift */; };
742CD4502444BE9400A9AB4C /* AnyPublisher+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60612443BAC30054C286 /* AnyPublisher+streamFromSpin.swift */; };
742CD4512444BE9400A9AB4C /* AnyPublisher+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60622443BAC30054C286 /* AnyPublisher+ReactiveStream.swift */; };
- 742CD4522444BE9400A9AB4C /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60632443BAC30054C286 /* Reducer.swift */; };
742CD4532444BE9400A9AB4C /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60642443BAC30054C286 /* UISpin.swift */; };
742CD4542444BE9400A9AB4C /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60652443BAC30054C286 /* Spin.swift */; };
- 742CD4552444BE9400A9AB4C /* AnyCancellable+DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60662443BAC30054C286 /* AnyCancellable+DisposeBag.swift */; };
742CD4562444BE9400A9AB4C /* AnyScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60672443BAC30054C286 /* AnyScheduler.swift */; };
742CD4572444BE9400A9AB4C /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60682443BAC30054C286 /* SwiftUISpin.swift */; };
742CD4582444BE9400A9AB4C /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60692443BAC30054C286 /* Feedback.swift */; };
@@ -132,27 +114,63 @@
747B70962444F29C00C863C2 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 747B70952444F29C00C863C2 /* ReactiveSwift.framework */; };
747B709A2444F2B000C863C2 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 747B70992444F2B000C863C2 /* ReactiveSwift.framework */; };
747B709E2444F2C700C863C2 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 747B709D2444F2C700C863C2 /* ReactiveSwift.framework */; };
+ 749A9C9424DCF9D70095032C /* ExecuterDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9124DCF9D60095032C /* ExecuterDefinition.swift */; };
+ 749A9C9524DCF9D70095032C /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9224DCF9D60095032C /* Reducer.swift */; };
+ 749A9C9624DCF9D70095032C /* AnySpinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9324DCF9D70095032C /* AnySpinner.swift */; };
+ 749A9C9B24DCFA130095032C /* Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9724DCFA0E0095032C /* Executer.swift */; };
+ 749A9C9C24DCFA130095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9824DCFA0E0095032C /* Spinner.swift */; };
+ 749A9C9D24DCFA140095032C /* Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9724DCFA0E0095032C /* Executer.swift */; };
+ 749A9C9E24DCFA140095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9824DCFA0E0095032C /* Spinner.swift */; };
+ 749A9C9F24DCFA150095032C /* Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9724DCFA0E0095032C /* Executer.swift */; };
+ 749A9CA024DCFA150095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9824DCFA0E0095032C /* Spinner.swift */; };
+ 749A9CA124DCFA150095032C /* Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9724DCFA0E0095032C /* Executer.swift */; };
+ 749A9CA224DCFA150095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9C9824DCFA0E0095032C /* Spinner.swift */; };
+ 749A9CA924DCFA340095032C /* Disposable+add.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA324DCFA2D0095032C /* Disposable+add.swift */; };
+ 749A9CAA24DCFA340095032C /* Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA424DCFA2D0095032C /* Executer.swift */; };
+ 749A9CAB24DCFA340095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA524DCFA2D0095032C /* Spinner.swift */; };
+ 749A9CAC24DCFA350095032C /* Disposable+add.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA324DCFA2D0095032C /* Disposable+add.swift */; };
+ 749A9CAD24DCFA350095032C /* Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA424DCFA2D0095032C /* Executer.swift */; };
+ 749A9CAE24DCFA350095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA524DCFA2D0095032C /* Spinner.swift */; };
+ 749A9CAF24DCFA350095032C /* Disposable+add.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA324DCFA2D0095032C /* Disposable+add.swift */; };
+ 749A9CB024DCFA350095032C /* Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA424DCFA2D0095032C /* Executer.swift */; };
+ 749A9CB124DCFA350095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA524DCFA2D0095032C /* Spinner.swift */; };
+ 749A9CB224DCFA360095032C /* Disposable+add.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA324DCFA2D0095032C /* Disposable+add.swift */; };
+ 749A9CB324DCFA360095032C /* Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA424DCFA2D0095032C /* Executer.swift */; };
+ 749A9CB424DCFA360095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CA524DCFA2D0095032C /* Spinner.swift */; };
+ 749A9CBD24DCFA4E0095032C /* DispatchQueue+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB724DCFA490095032C /* DispatchQueue+Executer.swift */; };
+ 749A9CBE24DCFA4E0095032C /* OperationQueue+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB624DCFA490095032C /* OperationQueue+Executer.swift */; };
+ 749A9CBF24DCFA4E0095032C /* RunLoop+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB824DCFA490095032C /* RunLoop+Executer.swift */; };
+ 749A9CC024DCFA4E0095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB524DCFA490095032C /* Spinner.swift */; };
+ 749A9CC124DCFA4F0095032C /* DispatchQueue+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB724DCFA490095032C /* DispatchQueue+Executer.swift */; };
+ 749A9CC224DCFA4F0095032C /* OperationQueue+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB624DCFA490095032C /* OperationQueue+Executer.swift */; };
+ 749A9CC324DCFA4F0095032C /* RunLoop+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB824DCFA490095032C /* RunLoop+Executer.swift */; };
+ 749A9CC424DCFA4F0095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB524DCFA490095032C /* Spinner.swift */; };
+ 749A9CC524DCFA4F0095032C /* DispatchQueue+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB724DCFA490095032C /* DispatchQueue+Executer.swift */; };
+ 749A9CC624DCFA4F0095032C /* OperationQueue+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB624DCFA490095032C /* OperationQueue+Executer.swift */; };
+ 749A9CC724DCFA4F0095032C /* RunLoop+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB824DCFA490095032C /* RunLoop+Executer.swift */; };
+ 749A9CC824DCFA4F0095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB524DCFA490095032C /* Spinner.swift */; };
+ 749A9CC924DCFA500095032C /* DispatchQueue+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB724DCFA490095032C /* DispatchQueue+Executer.swift */; };
+ 749A9CCA24DCFA500095032C /* OperationQueue+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB624DCFA490095032C /* OperationQueue+Executer.swift */; };
+ 749A9CCB24DCFA500095032C /* RunLoop+Executer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB824DCFA490095032C /* RunLoop+Executer.swift */; };
+ 749A9CCC24DCFA500095032C /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749A9CB524DCFA490095032C /* Spinner.swift */; };
74CB98182444E4280017C2E9 /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; };
74CB98222444EC900017C2E9 /* SpinCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 742CD35D2443FD2300A9AB4C /* SpinCommon.framework */; platformFilter = ios; };
74CB98292444EEE20017C2E9 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 74CB98272444EEE20017C2E9 /* RxSwift.framework */; };
74CB982B2444EEE20017C2E9 /* RxRelay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 74CB98282444EEE20017C2E9 /* RxRelay.framework */; };
74CB98302444EEFB0017C2E9 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 74CB982E2444EEFB0017C2E9 /* RxSwift.framework */; };
74CB98322444EEFB0017C2E9 /* RxRelay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 74CB982F2444EEFB0017C2E9 /* RxRelay.framework */; };
- 74FB60A42443BCDD0054C286 /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60592443BAC30054C286 /* Reducer.swift */; };
74FB60A52443BCDD0054C286 /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605A2443BAC30054C286 /* UISpin.swift */; };
74FB60A62443BCDD0054C286 /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605B2443BAC30054C286 /* Spin.swift */; };
74FB60A72443BCDD0054C286 /* Observable+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605C2443BAC30054C286 /* Observable+ReactiveStream.swift */; };
74FB60A82443BCDD0054C286 /* Observable+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605D2443BAC30054C286 /* Observable+streamFromSpin.swift */; };
74FB60A92443BCDD0054C286 /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605E2443BAC30054C286 /* SwiftUISpin.swift */; };
74FB60AA2443BCDD0054C286 /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605F2443BAC30054C286 /* Feedback.swift */; };
- 74FB60E52443C2790054C286 /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60592443BAC30054C286 /* Reducer.swift */; };
74FB60E62443C2790054C286 /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605A2443BAC30054C286 /* UISpin.swift */; };
74FB60E72443C2790054C286 /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605B2443BAC30054C286 /* Spin.swift */; };
74FB60E82443C2790054C286 /* Observable+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605C2443BAC30054C286 /* Observable+ReactiveStream.swift */; };
74FB60E92443C2790054C286 /* Observable+streamFromSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605D2443BAC30054C286 /* Observable+streamFromSpin.swift */; };
74FB60EA2443C2790054C286 /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605E2443BAC30054C286 /* SwiftUISpin.swift */; };
74FB60EB2443C2790054C286 /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605F2443BAC30054C286 /* Feedback.swift */; };
- 74FB61182443C40D0054C286 /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60592443BAC30054C286 /* Reducer.swift */; };
74FB61192443C40D0054C286 /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605A2443BAC30054C286 /* UISpin.swift */; };
74FB611A2443C40D0054C286 /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605B2443BAC30054C286 /* Spin.swift */; };
74FB611B2443C40D0054C286 /* Observable+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605C2443BAC30054C286 /* Observable+ReactiveStream.swift */; };
@@ -160,7 +178,6 @@
74FB611D2443C40D0054C286 /* SwiftUISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605E2443BAC30054C286 /* SwiftUISpin.swift */; };
74FB611E2443C40D0054C286 /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605F2443BAC30054C286 /* Feedback.swift */; };
74FB61282443C42B0054C286 /* Spin_RxSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 74FB61262443C42B0054C286 /* Spin_RxSwift.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 74FB61332443C4610054C286 /* Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB60592443BAC30054C286 /* Reducer.swift */; };
74FB61342443C4610054C286 /* UISpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605A2443BAC30054C286 /* UISpin.swift */; };
74FB61352443C4610054C286 /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605B2443BAC30054C286 /* Spin.swift */; };
74FB61362443C4610054C286 /* Observable+ReactiveStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FB605C2443BAC30054C286 /* Observable+ReactiveStream.swift */; };
@@ -296,30 +313,37 @@
747B70952444F29C00C863C2 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/Mac/ReactiveSwift.framework; sourceTree = ""; };
747B70992444F2B000C863C2 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/watchOS/ReactiveSwift.framework; sourceTree = ""; };
747B709D2444F2C700C863C2 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/tvOS/ReactiveSwift.framework; sourceTree = ""; };
+ 749A9C9124DCF9D60095032C /* ExecuterDefinition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExecuterDefinition.swift; sourceTree = ""; };
+ 749A9C9224DCF9D60095032C /* Reducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reducer.swift; sourceTree = ""; };
+ 749A9C9324DCF9D70095032C /* AnySpinner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnySpinner.swift; sourceTree = ""; };
+ 749A9C9724DCFA0E0095032C /* Executer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Executer.swift; sourceTree = ""; };
+ 749A9C9824DCFA0E0095032C /* Spinner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spinner.swift; sourceTree = ""; };
+ 749A9CA324DCFA2D0095032C /* Disposable+add.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Disposable+add.swift"; sourceTree = ""; };
+ 749A9CA424DCFA2D0095032C /* Executer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Executer.swift; sourceTree = ""; };
+ 749A9CA524DCFA2D0095032C /* Spinner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spinner.swift; sourceTree = ""; };
+ 749A9CB524DCFA490095032C /* Spinner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spinner.swift; sourceTree = ""; };
+ 749A9CB624DCFA490095032C /* OperationQueue+Executer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OperationQueue+Executer.swift"; sourceTree = ""; };
+ 749A9CB724DCFA490095032C /* DispatchQueue+Executer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Executer.swift"; sourceTree = ""; };
+ 749A9CB824DCFA490095032C /* RunLoop+Executer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RunLoop+Executer.swift"; sourceTree = ""; };
74CB98272444EEE20017C2E9 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; };
74CB98282444EEE20017C2E9 /* RxRelay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxRelay.framework; path = Carthage/Build/iOS/RxRelay.framework; sourceTree = ""; };
74CB982E2444EEFB0017C2E9 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/Mac/RxSwift.framework; sourceTree = ""; };
74CB982F2444EEFB0017C2E9 /* RxRelay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxRelay.framework; path = Carthage/Build/Mac/RxRelay.framework; sourceTree = ""; };
- 74FB60442443BAC30054C286 /* Reducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reducer.swift; sourceTree = ""; };
74FB60452443BAC30054C286 /* UISpin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISpin.swift; sourceTree = ""; };
74FB60462443BAC30054C286 /* Spin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spin.swift; sourceTree = ""; };
74FB60472443BAC30054C286 /* SignalProducer+Deferred.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SignalProducer+Deferred.swift"; sourceTree = ""; };
- 74FB60482443BAC30054C286 /* Disposable+DisposeBag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Disposable+DisposeBag.swift"; sourceTree = ""; };
74FB60492443BAC30054C286 /* SwiftUISpin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUISpin.swift; sourceTree = ""; };
74FB604A2443BAC30054C286 /* SignalProducer+ReactiveStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SignalProducer+ReactiveStream.swift"; sourceTree = ""; };
74FB604B2443BAC30054C286 /* SignalProducer+streamFromSpin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SignalProducer+streamFromSpin.swift"; sourceTree = ""; };
74FB604C2443BAC30054C286 /* Feedback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Feedback.swift; sourceTree = ""; };
74FB604E2443BAC30054C286 /* FeedbackDefinition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbackDefinition.swift; sourceTree = ""; };
- 74FB604F2443BAC30054C286 /* Spinner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spinner.swift; sourceTree = ""; };
74FB60502443BAC30054C286 /* SpinDefinition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpinDefinition.swift; sourceTree = ""; };
74FB60512443BAC30054C286 /* ReactiveStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactiveStream.swift; sourceTree = ""; };
74FB60522443BAC30054C286 /* Weakify.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Weakify.swift; sourceTree = ""; };
74FB60532443BAC30054C286 /* AnySpin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnySpin.swift; sourceTree = ""; };
74FB60542443BAC30054C286 /* StateRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateRenderer.swift; sourceTree = ""; };
- 74FB60552443BAC30054C286 /* ReducerDefinition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReducerDefinition.swift; sourceTree = ""; };
74FB60562443BAC30054C286 /* FeedbackDefinition+Default.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FeedbackDefinition+Default.swift"; sourceTree = ""; };
74FB60572443BAC30054C286 /* EventEmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventEmitter.swift; sourceTree = ""; };
- 74FB60592443BAC30054C286 /* Reducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reducer.swift; sourceTree = ""; };
74FB605A2443BAC30054C286 /* UISpin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISpin.swift; sourceTree = ""; };
74FB605B2443BAC30054C286 /* Spin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spin.swift; sourceTree = ""; };
74FB605C2443BAC30054C286 /* Observable+ReactiveStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+ReactiveStream.swift"; sourceTree = ""; };
@@ -328,10 +352,8 @@
74FB605F2443BAC30054C286 /* Feedback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Feedback.swift; sourceTree = ""; };
74FB60612443BAC30054C286 /* AnyPublisher+streamFromSpin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AnyPublisher+streamFromSpin.swift"; sourceTree = "