Skip to content

Commit 6c13748

Browse files
authored
Merge pull request #64 from RxSwiftCommunity/not-replay-execution
Not replay executionObservables for execute()
2 parents 0abe2a7 + f15eebb commit 6c13748

File tree

2 files changed

+97
-27
lines changed

2 files changed

+97
-27
lines changed

Sources/Action/Action.swift

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public final class Action<Input, Element> {
7272
return Observable.empty()
7373
}
7474
}
75-
.shareReplay(1)
75+
.share()
7676

7777
elements = executionObservables
7878
.flatMap { $0.catchError { _ in Observable.empty() } }
@@ -120,20 +120,26 @@ public final class Action<Input, Element> {
120120

121121
@discardableResult
122122
public func execute(_ value: Input) -> Observable<Element> {
123-
let subject = ReplaySubject<Element>.createUnbounded()
124-
125-
executionObservables
123+
defer {
124+
inputs.onNext(value)
125+
}
126+
127+
let execution = executionObservables
126128
.take(1)
127-
.flatMap { $0.catchError { _ in Observable.never() } }
128-
.bindTo(subject)
129-
.addDisposableTo(disposeBag)
129+
.flatMap { $0 }
130+
.catchError { throw ActionError.underlyingError($0) }
130131

131-
errors
132-
.map { throw $0 }
133-
.bindTo(subject)
134-
.addDisposableTo(disposeBag)
132+
let notEnabledError = inputs
133+
.takeUntil(executionObservables)
134+
.withLatestFrom(enabled)
135+
.flatMap { $0 ? Observable<Element>.empty() : Observable.error(ActionError.notEnabled) }
135136

136-
inputs.onNext(value)
137+
let subject = ReplaySubject<Element>.createUnbounded()
138+
Observable
139+
.of(execution, notEnabledError)
140+
.merge()
141+
.subscribe(subject)
142+
.addDisposableTo(disposeBag)
137143

138144
return subject.asObservable()
139145
}

Tests/ActionTests/ActionTests.swift

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ class ActionTests: QuickSpec {
374374
executionObservables = scheduler.createObserver(Observable<String>.self)
375375
}
376376

377-
func bindAndExecute(action: Action<String, String>) {
377+
func bindAndExecuteTwice(action: Action<String, String>) {
378378
action.executionObservables
379379
.bindTo(executionObservables)
380380
.addDisposableTo(disposeBag)
@@ -385,80 +385,144 @@ class ActionTests: QuickSpec {
385385
.addDisposableTo(disposeBag)
386386
}
387387

388+
scheduler.scheduleAt(20) {
389+
action.execute("b")
390+
.bindTo(element)
391+
.addDisposableTo(disposeBag)
392+
}
393+
388394
scheduler.start()
389395
}
390396

391397
context("single element action") {
392398
beforeEach {
393399
action = Action { Observable.just($0) }
394-
bindAndExecute(action: action)
400+
bindAndExecuteTwice(action: action)
395401
}
396402

397-
it("element receives single value") {
403+
it("element receives single value for each execution") {
398404
XCTAssertEqual(element.events, [
399405
next(10, "a"),
400406
completed(10),
407+
next(20, "b"),
408+
completed(20),
401409
])
402410
}
403411

404-
it("executes once") {
405-
expect(executionObservables.events.count) == 1
412+
it("executes twice") {
413+
expect(executionObservables.events.count) == 2
406414
}
407415
}
408416

409417
context("multiple element action") {
410418
beforeEach {
411419
action = Action { Observable.of($0, $0, $0) }
412-
bindAndExecute(action: action)
420+
bindAndExecuteTwice(action: action)
413421
}
414422

415-
it("element receives mutiple values") {
423+
it("element receives 3 values for each execution") {
416424
XCTAssertEqual(element.events, [
417425
next(10, "a"),
418426
next(10, "a"),
419427
next(10, "a"),
420428
completed(10),
429+
next(20, "b"),
430+
next(20, "b"),
431+
next(20, "b"),
432+
completed(20),
421433
])
422434
}
423435

424-
it("executes once") {
425-
expect(executionObservables.events.count) == 1
436+
it("executes twice") {
437+
expect(executionObservables.events.count) == 2
426438
}
427439
}
428440

429441
context("error action") {
430442
beforeEach {
431443
action = Action { _ in Observable.error(TestError) }
432-
bindAndExecute(action: action)
444+
bindAndExecuteTwice(action: action)
433445
}
434446

435447
it("element fails with underlyingError") {
436448
XCTAssertEqual(element.events, [
437-
error(10, ActionError.underlyingError(TestError))
449+
error(10, ActionError.underlyingError(TestError)),
450+
error(20, ActionError.underlyingError(TestError)),
438451
])
439452
}
440453

441-
it("executes once") {
442-
expect(executionObservables.events.count) == 1
454+
it("executes twice") {
455+
expect(executionObservables.events.count) == 2
443456
}
444457
}
445458

446459
context("disabled") {
447460
beforeEach {
448461
action = Action(enabledIf: Observable.just(false)) { Observable.just($0) }
449-
bindAndExecute(action: action)
462+
bindAndExecuteTwice(action: action)
450463
}
451464

452465
it("element fails with notEnabled") {
453466
XCTAssertEqual(element.events, [
454-
error(10, ActionError.notEnabled)
467+
error(10, ActionError.notEnabled),
468+
error(20, ActionError.notEnabled),
455469
])
456470
}
457471

458472
it("never executes") {
459473
expect(executionObservables.events).to(beEmpty())
460474
}
461475
}
476+
477+
context("execute while executing") {
478+
var secondElement: TestableObserver<String>!
479+
var trigger: PublishSubject<Void>!
480+
481+
beforeEach {
482+
secondElement = scheduler.createObserver(String.self)
483+
trigger = PublishSubject<Void>()
484+
action = Action { Observable.just($0).sample(trigger) }
485+
486+
action.executionObservables
487+
.bindTo(executionObservables)
488+
.addDisposableTo(disposeBag)
489+
490+
scheduler.scheduleAt(10) {
491+
action.execute("a")
492+
.bindTo(element)
493+
.addDisposableTo(disposeBag)
494+
}
495+
496+
scheduler.scheduleAt(20) {
497+
action.execute("b")
498+
.bindTo(secondElement)
499+
.addDisposableTo(disposeBag)
500+
}
501+
502+
scheduler.scheduleAt(30) {
503+
trigger.onNext()
504+
}
505+
506+
scheduler.start()
507+
}
508+
509+
it("first element receives single value") {
510+
XCTAssertEqual(element.events, [
511+
next(30, "a"),
512+
completed(30),
513+
])
514+
}
515+
516+
it("second element fails with notEnabled error") {
517+
XCTAssertEqual(secondElement.events, [
518+
error(20, ActionError.notEnabled)
519+
])
520+
}
521+
522+
it("executes once") {
523+
expect(executionObservables.events.count) == 1
524+
}
525+
}
462526
}
463527
}
464528
}

0 commit comments

Comments
 (0)