Skip to content

Commit b0d817e

Browse files
authored
Merge pull request #13 from trading-point/michael/iflet
Fix issues with ifLet sending "else" values too often.
2 parents 848e73f + a489b3d commit b0d817e

File tree

2 files changed

+122
-3
lines changed

2 files changed

+122
-3
lines changed

Sources/ComposableArchitecture/UIKit/IfLetUIKit.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Combine
21
import ReactiveSwift
32

43
extension Store {
@@ -43,16 +42,28 @@ extension Store {
4342
then unwrap: @escaping (Store<Wrapped, Action>) -> Void,
4443
else: @escaping () -> Void
4544
) -> Disposable where State == Wrapped? {
46-
self
45+
let elseDisposable = self
46+
.scope(
47+
state: { state -> Effect<Wrapped?, Never> in
48+
state
49+
.skipRepeats { ($0 != nil) == ($1 != nil) }
50+
}
51+
)
52+
.startWithValues { store in
53+
if store.$state.value == nil { `else`() }
54+
}
55+
56+
let unwrapDisposable = self
4757
.scope(
4858
state: { state -> Effect<Wrapped, Never> in
4959
state
5060
.skipRepeats { ($0 != nil) == ($1 != nil) }
51-
.on(value: { if $0 == nil { `else`() } })
5261
.compactMap { $0 }
5362
}
5463
)
5564
.startWithValues(unwrap)
65+
66+
return CompositeDisposable([elseDisposable, unwrapDisposable])
5667
}
5768

5869
/// An overload of `ifLet(then:else:)` for the times that you do not want to handle the `else`

Tests/ComposableArchitectureTests/StoreTests.swift

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,112 @@ final class StoreTests: XCTestCase {
188188
store.send(.incr)
189189
XCTAssertEqual(ViewStore(store).state, 100_000)
190190
}
191+
192+
func testPublisherScope() {
193+
let appReducer = Reducer<Int, Bool, Void> { state, action, _ in
194+
state += action ? 1 : 0
195+
return .none
196+
}
197+
198+
let parentStore = Store(initialState: 0, reducer: appReducer, environment: ())
199+
200+
var outputs: [Int] = []
201+
202+
parentStore
203+
.scope { $0.skipRepeats() }
204+
.startWithValues { outputs.append($0.$state.value) }
205+
206+
XCTAssertEqual(outputs, [0])
207+
208+
parentStore.send(true)
209+
XCTAssertEqual(outputs, [0, 1])
210+
211+
parentStore.send(false)
212+
XCTAssertEqual(outputs, [0, 1])
213+
parentStore.send(false)
214+
XCTAssertEqual(outputs, [0, 1])
215+
parentStore.send(false)
216+
XCTAssertEqual(outputs, [0, 1])
217+
parentStore.send(false)
218+
XCTAssertEqual(outputs, [0, 1])
219+
}
220+
221+
func testIfLetAfterScope() {
222+
struct AppState {
223+
var count: Int?
224+
}
225+
226+
let appReducer = Reducer<AppState, Int?, Void> { state, action, _ in
227+
state.count = action
228+
return .none
229+
}
230+
231+
let parentStore = Store(initialState: AppState(), reducer: appReducer, environment: ())
232+
233+
// NB: This test needs to hold a strong reference to the emitted stores
234+
var outputs: [Int?] = []
235+
var stores: [Any] = []
236+
237+
parentStore
238+
.scope(state: \.count)
239+
.ifLet(
240+
then: { store in
241+
stores.append(store)
242+
outputs.append(store.$state.value)
243+
},
244+
else: {
245+
outputs.append(nil)
246+
})
247+
248+
XCTAssertEqual(outputs, [nil])
249+
250+
parentStore.send(1)
251+
XCTAssertEqual(outputs, [nil, 1])
252+
253+
parentStore.send(nil)
254+
XCTAssertEqual(outputs, [nil, 1, nil])
255+
256+
parentStore.send(1)
257+
XCTAssertEqual(outputs, [nil, 1, nil, 1])
258+
259+
parentStore.send(nil)
260+
XCTAssertEqual(outputs, [nil, 1, nil, 1, nil])
261+
262+
parentStore.send(1)
263+
XCTAssertEqual(outputs, [nil, 1, nil, 1, nil, 1])
264+
265+
parentStore.send(nil)
266+
XCTAssertEqual(outputs, [nil, 1, nil, 1, nil, 1, nil])
267+
}
268+
269+
func testIfLetTwo() {
270+
let parentStore = Store(
271+
initialState: 0,
272+
reducer: Reducer<Int?, Bool, Void> { state, action, _ in
273+
if action {
274+
state? += 1
275+
return .none
276+
} else {
277+
return Effect(value: true).observe(on: QueueScheduler.main)
278+
}
279+
},
280+
environment: ()
281+
)
282+
283+
parentStore.ifLet { childStore in
284+
let vs = ViewStore(childStore)
285+
286+
vs
287+
.publisher.producer
288+
.startWithValues { _ in }
289+
290+
vs.send(false)
291+
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
292+
vs.send(false)
293+
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
294+
vs.send(false)
295+
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
296+
XCTAssertEqual(vs.state, 3)
297+
}
298+
}
191299
}

0 commit comments

Comments
 (0)