@@ -7,12 +7,16 @@ import ReactiveSwift
7
7
/// You will typically construct a single one of these at the root of your application, and then use
8
8
/// the `scope` method to derive more focused stores that can be passed to subviews.
9
9
public final class Store < State, Action> {
10
- @MutableProperty private( set) var state : State
10
+ @MutableProperty
11
+ private( set) var state : State
12
+
11
13
private var isSending = false
12
14
private let reducer : ( inout State , Action ) -> Effect < Action , Never >
13
15
private var synchronousActionsToSend : [ Action ] = [ ]
14
16
private var bufferedActions : [ Action ] = [ ]
15
-
17
+ internal var effectDisposables : [ UUID : Disposable ] = [ : ]
18
+ internal var parentDisposable : Disposable ?
19
+
16
20
/// Initializes a store from an initial state, a reducer, and an environment.
17
21
///
18
22
/// - Parameters:
@@ -29,7 +33,7 @@ public final class Store<State, Action> {
29
33
reducer: { reducer. run ( & $0, $1, environment) }
30
34
)
31
35
}
32
-
36
+
33
37
/// Scopes the store to one that exposes local state and actions.
34
38
///
35
39
/// This can be useful for deriving new stores to hand to child views in an application. For
@@ -170,11 +174,12 @@ public final class Store<State, Action> {
170
174
return . none
171
175
}
172
176
)
173
- self . $state. producer
174
- . startWithValues { [ weak localStore] newValue in localStore? . state = toLocalState ( newValue) }
177
+ localStore. parentDisposable = self . $state. producer. startWithValues { [ weak localStore] state in
178
+ localStore? . state = toLocalState ( state)
179
+ }
175
180
return localStore
176
181
}
177
-
182
+
178
183
/// Scopes the store to one that exposes local state.
179
184
///
180
185
/// - Parameter toLocalState: A function that transforms `State` into `LocalState`.
@@ -184,7 +189,7 @@ public final class Store<State, Action> {
184
189
) -> Store < LocalState , Action > {
185
190
self . scope ( state: toLocalState, action: { $0 } )
186
191
}
187
-
192
+
188
193
/// Scopes the store to a producer of stores of more local state and local actions.
189
194
///
190
195
/// - Parameters:
@@ -196,14 +201,14 @@ public final class Store<State, Action> {
196
201
state toLocalState: @escaping ( Effect < State , Never > ) -> Effect < LocalState , Never > ,
197
202
action fromLocalAction: @escaping ( LocalAction ) -> Action
198
203
) -> Effect < Store < LocalState , LocalAction > , Never > {
199
-
204
+
200
205
func extractLocalState( _ state: State ) -> LocalState ? {
201
206
var localState : LocalState ?
202
207
_ = toLocalState ( Effect ( value: state) )
203
208
. startWithValues { localState = $0 }
204
209
return localState
205
210
}
206
-
211
+
207
212
return toLocalState ( self . $state. producer)
208
213
. map { localState in
209
214
let localStore = Store < LocalState , LocalAction > (
@@ -212,17 +217,17 @@ public final class Store<State, Action> {
212
217
self . send ( fromLocalAction ( localAction) )
213
218
localState = extractLocalState ( self . state) ?? localState
214
219
return . none
215
- } )
216
-
217
- self . $state. producer
218
- . startWithValues { [ weak localStore] state in
219
- guard let localStore = localStore else { return }
220
- localStore. state = extractLocalState ( state) ?? localStore. state
221
220
}
221
+ )
222
+
223
+ localStore. parentDisposable = self . $state. producer. startWithValues { [ weak localStore] state in
224
+ guard let localStore = localStore else { return }
225
+ localStore. state = extractLocalState ( state) ?? localStore. state
226
+ }
222
227
return localStore
223
228
}
224
229
}
225
-
230
+
226
231
/// Scopes the store to a producer of stores of more local state and local actions.
227
232
///
228
233
/// - Parameter toLocalState: A function that transforms a producer of `State` into a producer
@@ -234,66 +239,95 @@ public final class Store<State, Action> {
234
239
) -> Effect < Store < LocalState , Action > , Never > {
235
240
self . producerScope ( state: toLocalState, action: { $0 } )
236
241
}
237
-
242
+
238
243
func send( _ action: Action ) {
239
244
if !self . isSending {
240
245
self . synchronousActionsToSend. append ( action)
241
246
} else {
242
247
self . bufferedActions. append ( action)
243
248
return
244
249
}
245
-
250
+
246
251
while !self . synchronousActionsToSend. isEmpty || !self . bufferedActions. isEmpty {
247
252
let action =
248
253
!self . synchronousActionsToSend. isEmpty
249
254
? self . synchronousActionsToSend. removeFirst ( )
250
255
: self . bufferedActions. removeFirst ( )
251
-
256
+
252
257
self . isSending = true
253
258
let effect = self . reducer ( & self . state, action)
254
259
self . isSending = false
255
-
260
+
261
+ var didComplete = false
262
+ let effectID = UUID ( )
263
+
256
264
var isProcessingEffects = true
257
- effect. startWithValues { [ weak self] action in
258
- if isProcessingEffects {
259
- self ? . synchronousActionsToSend. append ( action)
260
- } else {
261
- self ? . send ( action)
265
+
266
+ let observer = Signal < Action , Never > . Observer (
267
+ value: { [ weak self] action in
268
+ if isProcessingEffects {
269
+ self ? . synchronousActionsToSend. append ( action)
270
+ } else {
271
+ self ? . send ( action)
272
+ }
273
+ } ,
274
+ failed: . none,
275
+ completed: { [ weak self] in
276
+ didComplete = true
277
+ self ? . effectDisposables. removeValue ( forKey: effectID) ? . dispose ( )
278
+ } ,
279
+ interrupted: { [ weak self] in
280
+ didComplete = true
281
+ self ? . effectDisposables. removeValue ( forKey: effectID) ? . dispose ( )
262
282
}
263
- }
283
+ )
284
+ let effectDisposable = effect. start ( observer)
264
285
isProcessingEffects = false
286
+
287
+ if !didComplete {
288
+ self . effectDisposables [ effectID] = effectDisposable
289
+ } else {
290
+ effectDisposable. dispose ( )
291
+ }
265
292
}
266
293
}
267
-
294
+
268
295
/// Returns a "stateless" store by erasing state to `Void`.
269
296
public var stateless : Store < Void , Action > {
270
297
self . scope ( state: { _ in ( ) } )
271
298
}
272
-
299
+
273
300
/// Returns an "actionless" store by erasing action to `Never`.
274
301
public var actionless : Store < State , Never > {
275
302
func absurd< A> ( _ never: Never ) -> A { }
276
303
return self . scope ( state: { $0 } , action: absurd)
277
304
}
278
-
305
+
279
306
private init (
280
307
initialState: State ,
281
308
reducer: @escaping ( inout State , Action ) -> Effect < Action , Never >
282
309
) {
283
310
self . reducer = reducer
284
311
self . state = initialState
285
312
}
313
+
314
+ deinit {
315
+ self . parentDisposable? . dispose ( )
316
+ self . effectDisposables. keys. forEach { id in
317
+ self . effectDisposables. removeValue ( forKey: id) ? . dispose ( )
318
+ }
319
+ }
286
320
}
287
321
288
322
/// A producer of store state.
289
323
@dynamicMemberLookup
290
324
public struct Produced < Value> : SignalProducerConvertible {
291
325
public let producer : Effect < Value , Never >
292
-
326
+
293
327
init ( by upstream: Effect < Value , Never > ) {
294
328
self . producer = upstream
295
329
}
296
-
330
+
297
331
/// Returns the resulting producer of a given key path.
298
332
public subscript< LocalValue> (
299
333
dynamicMember keyPath: KeyPath < Value , LocalValue >
@@ -303,9 +337,9 @@ public struct Produced<Value>: SignalProducerConvertible {
303
337
}
304
338
305
339
@available (
306
- * , deprecated,
307
- message:
308
- """
340
+ * , deprecated,
341
+ message:
342
+ """
309
343
Consider using `Produced<State>` instead, this typealias is added for backward compatibility and will be removed in the next major release.
310
344
"""
311
345
)
0 commit comments