Skip to content

Commit 82346a5

Browse files
committed
Save commands until they can execute or expire
1 parent 94eda9a commit 82346a5

File tree

1 file changed

+68
-6
lines changed

1 file changed

+68
-6
lines changed

Sources/Reactor.swift

+68-6
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,46 @@ public protocol Event {}
1515

1616
// MARK: - Commands
1717

18-
public protocol Command {
18+
public protocol AnyCommand {
19+
func _execute(state: Any, core: Any)
20+
func _canExecute(state: Any) -> Bool
21+
var expiresAfter: TimeInterval { get }
22+
}
23+
24+
extension AnyCommand {
25+
var expiresAfter: TimeInterval { return 10.0 }
26+
}
27+
28+
public protocol Command: AnyCommand {
1929
associatedtype StateType: State
2030
func execute(state: StateType, core: Core<StateType>)
31+
func canExecute(state: StateType) -> Bool
32+
}
33+
34+
extension Command {
35+
36+
public func canExecute(state: StateType) -> Bool {
37+
return true
38+
}
39+
40+
public func _canExecute(state: Any) -> Bool {
41+
if let state = state as? StateType {
42+
return canExecute(state: state)
43+
} else {
44+
return false
45+
}
46+
}
47+
48+
public func _execute(state: Any, core: Any) {
49+
if let state = state as? StateType, let core = core as? Core<StateType> {
50+
execute(state: state, core: core)
51+
}
52+
}
53+
}
54+
55+
public struct Commands<StateType: State> {
56+
private(set) var expiresAt: Date
57+
private(set) var command: AnyCommand
2158
}
2259

2360

@@ -88,20 +125,33 @@ public class Core<StateType: State> {
88125

89126
private let jobQueue:DispatchQueue = DispatchQueue(label: "reactor.core.queue", qos: .userInitiated, attributes: [])
90127

91-
private let subscriptionsSyncQueue = DispatchQueue(label: "reactor.core.subscription.sync")
128+
private let internalSyncQueue = DispatchQueue(label: "reactor.core.internal.sync")
92129
private var _subscriptions = [Subscription<StateType>]()
93130
private var subscriptions: [Subscription<StateType>] {
94131
get {
95-
return subscriptionsSyncQueue.sync {
132+
return internalSyncQueue.sync {
96133
return self._subscriptions
97134
}
98135
}
99136
set {
100-
subscriptionsSyncQueue.sync {
137+
internalSyncQueue.sync {
101138
self._subscriptions = newValue
102139
}
103140
}
104141
}
142+
private var _commands = [Commands<StateType>]()
143+
private var commands: [Commands<StateType>] {
144+
get {
145+
return internalSyncQueue.sync {
146+
return self._commands
147+
}
148+
}
149+
set {
150+
internalSyncQueue.sync {
151+
self._commands = newValue
152+
}
153+
}
154+
}
105155

106156
private let middlewares: [Middlewares<StateType>]
107157
public private (set) var state: StateType {
@@ -140,13 +190,25 @@ public class Core<StateType: State> {
140190
jobQueue.async {
141191
self.state.react(to: event)
142192
let state = self.state
193+
let executable = self.commands.enumerated().filter { $1.command._canExecute(state: state) }
194+
executable.forEach {
195+
self.commands.remove(at: $0)
196+
$1.command._execute(state: state, core: self)
197+
}
198+
let now = Date()
199+
let expired = self.commands.enumerated().filter { $1.expiresAt < now }
200+
expired.forEach { self.commands.remove(at: $0.offset) }
143201
self.middlewares.forEach { $0.middleware._process(event: event, state: state) }
144202
}
145203
}
146204

147205
public func fire<C: Command>(command: C) where C.StateType == StateType {
148-
jobQueue.async {
149-
command.execute(state: self.state, core: self)
206+
if command.canExecute(state: state) {
207+
jobQueue.async {
208+
command.execute(state: self.state, core: self)
209+
}
210+
} else {
211+
commands.append(Commands(expiresAt: Date().addingTimeInterval(command.expiresAfter), command: command))
150212
}
151213
}
152214

0 commit comments

Comments
 (0)