Skip to content

Commit 63c9203

Browse files
committed
Add asynchronous effect handling routes
1 parent e17c28c commit 63c9203

File tree

6 files changed

+714
-7
lines changed

6 files changed

+714
-7
lines changed

MobiusCore/Source/EffectHandlers/EffectExecutor.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Foundation
1616

1717
final class EffectExecutor<Effect, Event>: Connectable {
1818
private let handleEffect: (Effect, EffectCallback<Event>) -> Disposable
19+
private let outputQueue: DispatchQueue?
1920
private var output: Consumer<Event>?
2021

2122
private let lock = Lock()
@@ -26,8 +27,9 @@ final class EffectExecutor<Effect, Event>: Connectable {
2627
private var handlingEffects: [Int64: EffectHandlingState<Event>] = [:]
2728
private var nextID = Int64(0)
2829

29-
init(handleInput: @escaping (Effect, EffectCallback<Event>) -> Disposable) {
30+
init(handleInput: @escaping (Effect, EffectCallback<Event>) -> Disposable, outputQueue: DispatchQueue? = nil) {
3031
self.handleEffect = handleInput
32+
self.outputQueue = outputQueue
3133
}
3234

3335
func connect(_ consumer: @escaping Consumer<Event>) -> Connection<Effect> {
@@ -58,7 +60,13 @@ final class EffectExecutor<Effect, Event>: Connectable {
5860
let callback = EffectCallback(
5961
// Any events produced as a result of handling the effect will be sent to this class's `output` consumer,
6062
// unless it has already been disposed.
61-
onSend: { [weak self] event in self?.output?(event) },
63+
onSend: { [weak self] event in
64+
if let outputQueue = self?.outputQueue {
65+
outputQueue.async { self?.output?(event) }
66+
} else {
67+
self?.output?(event)
68+
}
69+
},
6270
// Once an effect has been handled, remove the reference to its callback and disposable.
6371
onEnd: { [weak self] in self?.delete(id: id) }
6472
)

MobiusCore/Source/EffectHandlers/EffectRouter.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public struct EffectRouter<Effect, Event> {
5959
public func routeEffects<EffectParameters>(
6060
withParameters extractParameters: @escaping (Effect) -> EffectParameters?
6161
) -> _PartialEffectRouter<Effect, EffectParameters, Event> {
62-
return _PartialEffectRouter(routes: routes, path: extractParameters, queue: nil)
62+
return _PartialEffectRouter(routes: routes, path: extractParameters, queue: nil, receiveQueue: nil)
6363
}
6464

6565
/// Convert this `EffectRouter` into `Connectable` which can be attached to a Mobius Loop, or called on its own to
@@ -77,14 +77,15 @@ public struct _PartialEffectRouter<Effect, EffectParameters, Event> {
7777
fileprivate let routes: [Route<Effect, Event>]
7878
fileprivate let path: (Effect) -> EffectParameters?
7979
fileprivate let queue: DispatchQueue?
80+
fileprivate let receiveQueue: DispatchQueue?
8081

8182
/// Route to an `EffectHandler`.
8283
///
8384
/// - Parameter effectHandler: the `EffectHandler` for the route in question.
8485
public func to<Handler: EffectHandler>(
8586
_ effectHandler: Handler
8687
) -> EffectRouter<Effect, Event> where Handler.EffectParameters == EffectParameters, Handler.Event == Event {
87-
let connectable = EffectExecutor(handleInput: effectHandler.handle)
88+
let connectable = EffectExecutor(handleInput: effectHandler.handle, outputQueue: receiveQueue)
8889
let route = Route<Effect, Event>(extractParameters: path, connectable: connectable, queue: queue)
8990
return EffectRouter(routes: routes + [route])
9091
}
@@ -100,7 +101,7 @@ public struct _PartialEffectRouter<Effect, EffectParameters, Event> {
100101
return EffectRouter(routes: routes + [route])
101102
}
102103

103-
/// Handle an the current `Effect` asynchronously on the provided `DispatchQueue`
104+
/// Handle the current `Effect` asynchronously on the provided `DispatchQueue`
104105
///
105106
/// Warning: Dispatching events to a loop from a different queue is not a thread-safe operation and will require
106107
/// manual synchronization unless the loop is run in a `MobiusController`.
@@ -109,7 +110,14 @@ public struct _PartialEffectRouter<Effect, EffectParameters, Event> {
109110
///
110111
/// - Parameter queue: The `DispatchQueue` that the current `Effect` should be handled on.
111112
public func on(queue: DispatchQueue) -> Self {
112-
return Self(routes: routes, path: path, queue: queue)
113+
return Self(routes: routes, path: path, queue: queue, receiveQueue: receiveQueue)
114+
}
115+
116+
/// Receive an `Event` asynchronously on the provided `DispatchQueue`
117+
///
118+
/// - Parameter queue: The `DispatchQueue` that any `Event` should be received on.
119+
public func receiveOn(queue receiveQueue: DispatchQueue) -> Self {
120+
return Self(routes: routes, path: path, queue: queue, receiveQueue: receiveQueue)
113121
}
114122
}
115123

0 commit comments

Comments
 (0)