diff --git a/RxFlow/FlowCoordinator.swift b/RxFlow/FlowCoordinator.swift index d00b55f..4f9203b 100644 --- a/RxFlow/FlowCoordinator.swift +++ b/RxFlow/FlowCoordinator.swift @@ -46,13 +46,11 @@ public final class FlowCoordinator: NSObject { // listen for the internal steps relay that aggregates the flow's Stepper's steps and // the FlowContributors's Stepper's steps self.stepsRelay - .take(until: allowStepWhenDismissed ? .empty() : flow.rxDismissed.asObservable()) .do(onDispose: { [weak self] in self?.childFlowCoordinators.removeAll() self?.parentFlowCoordinator?.childFlowCoordinators.removeValue(forKey: self?.identifier ?? "") }) - .asSignal(onErrorJustReturn: NoneStep()) - .flatMapLatest { flow.adapt(step: $0).asSignal(onErrorJustReturn: NoneStep()) } + .flatMapLatest { flow.adapt(step: $0) } .do(onNext: { [weak self] in self?.willNavigateRelay.accept((flow, $0)) }) .map { return (flowContributors: flow.navigate(to: $0), step: $0) } .do(onNext: { [weak self] in self?.didNavigateRelay.accept((flow, $0.step)) }) @@ -98,6 +96,8 @@ public final class FlowCoordinator: NSObject { .flatMap { [weak self] in self?.steps(from: $0, within: flow, allowStepWhenDismissed: allowStepWhenDismissed) ?? Signal.empty() } + .take(until: allowStepWhenDismissed ? .empty() : flow.rxDismissed.asObservable()) + .asSignal(onErrorJustReturn: NoneStep()) .emit(to: self.stepsRelay) .disposed(by: self.disposeBag) diff --git a/RxFlowTests/FlowCoordinatorTests.swift b/RxFlowTests/FlowCoordinatorTests.swift index 661dd6e..5d7b5cb 100644 --- a/RxFlowTests/FlowCoordinatorTests.swift +++ b/RxFlowTests/FlowCoordinatorTests.swift @@ -207,6 +207,28 @@ final class TestDismissedFlow: Flow { } } +final class TestLeakingFlow: Flow { + var root: Presentable = UIViewController() + + var rxDismissed: Single { rxDismissedRelay.take(1).asSingle() } + let rxDismissedRelay = PublishRelay() + + func navigate(to step: Step) -> FlowContributors { + guard let step = step as? TestSteps else { return .none } + + switch step { + case .one: + let flowContributor = FlowContributor.contribute( + withNextPresentable: UIViewController(), + withNextStepper: DefaultStepper() + ) + return .one(flowContributor: flowContributor) + default: + return .none + } + } +} + final class FlowCoordinatorTests: XCTestCase { func testCoordinateWithOneStepper() { @@ -320,6 +342,31 @@ final class FlowCoordinatorTests: XCTestCase { let recordedSteps = try? dismissedFlow.recordedSteps.take(3).toBlocking().toArray() XCTAssertEqual(recordedSteps, [.one, .two, .three]) } + + func testFlowIsNotLeakingWhenHasOneStep() throws { + weak var leakingFlowReference: TestLeakingFlow? + let exp = expectation(description: "Flow when ready") + let flowCoordinator = FlowCoordinator() + + withExtendedLifetime(TestLeakingFlow()) { leakingFlow in + leakingFlowReference = leakingFlow + + flowCoordinator.coordinate(flow: leakingFlow, + with: OneStepper(withSingleStep: TestSteps.one)) + + Flows.use(leakingFlow, when: .created) { (_) in + exp.fulfill() + } + } + + waitForExpectations(timeout: 1) + + XCTAssertNotNil(leakingFlowReference) + + try XCTUnwrap(leakingFlowReference).rxDismissedRelay.accept(Void()) + + XCTAssertNil(leakingFlowReference) + } } #endif