Skip to content

Commit 73f36fb

Browse files
committed
flowcoordinator: navigate on main thread
1 parent 6b7982c commit 73f36fb

7 files changed

+78
-6
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
** Version 2.12.2 **:
2+
3+
- ensure the navigate function is called on the main thread (regression introduced in 2.12.1)
4+
15
** Version 2.12.1 **:
26

37
- fix a possible memory leak when the Coordinator's lifecycle was unexpectedly longer than the flow ones (thanks to @asiliuk)

RxFlow.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "RxFlow"
3-
s.version = "2.12.1"
3+
s.version = "2.12.2"
44
s.swift_version = '5.3'
55
s.summary = "RxFlow is a navigation framework for iOS applications, based on a Reactive Coordinator pattern."
66

RxFlow.xcodeproj/project.pbxproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@
604604
"@executable_path/Frameworks",
605605
"@loader_path/Frameworks",
606606
);
607-
MARKETING_VERSION = 2.12.1;
607+
MARKETING_VERSION = 2.12.2;
608608
PRODUCT_BUNDLE_IDENTIFIER = io.warpfactor.RxFlow;
609609
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
610610
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -639,7 +639,7 @@
639639
"@executable_path/Frameworks",
640640
"@loader_path/Frameworks",
641641
);
642-
MARKETING_VERSION = 2.12.1;
642+
MARKETING_VERSION = 2.12.2;
643643
PRODUCT_BUNDLE_IDENTIFIER = io.warpfactor.RxFlow;
644644
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
645645
PROVISIONING_PROFILE_SPECIFIER = "";

RxFlow/FlowCoordinator.swift

+5-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public final class FlowCoordinator: NSObject {
5050
self?.childFlowCoordinators.removeAll()
5151
self?.parentFlowCoordinator?.childFlowCoordinators.removeValue(forKey: self?.identifier ?? "")
5252
})
53-
.flatMapLatest { flow.adapt(step: $0) }
53+
.asSignal(onErrorJustReturn: NoneStep())
54+
.flatMapLatest { flow.adapt(step: $0).asSignal(onErrorJustReturn: NoneStep()) }
5455
.do(onNext: { [weak self] in self?.willNavigateRelay.accept((flow, $0)) })
5556
.map { return (flowContributors: flow.navigate(to: $0), step: $0) }
5657
.do(onNext: { [weak self] in self?.didNavigateRelay.accept((flow, $0.step)) })
@@ -77,8 +78,9 @@ public final class FlowCoordinator: NSObject {
7778
.do(onNext: { [weak self] presentableAndSteppers in
7879
self?.setReadiness(for: flow, basedOn: presentableAndSteppers.map { $0.presentable })
7980
})
80-
// transforms a FlowContributors in a sequence of individual FlowContributor
81+
8182
.flatMap { Signal.from($0) }
83+
// transforms a FlowContributors in a sequence of individual FlowContributor
8284
// the FlowContributor is related to a new Flow, we coordinate this new Flow
8385
.do(onNext: { [weak self] presentableAndStepper in
8486
if let childFlow = presentableAndStepper.presentable as? Flow {
@@ -96,6 +98,7 @@ public final class FlowCoordinator: NSObject {
9698
.flatMap { [weak self] in
9799
self?.steps(from: $0, within: flow, allowStepWhenDismissed: allowStepWhenDismissed) ?? Signal.empty()
98100
}
101+
.asObservable()
99102
.take(until: allowStepWhenDismissed ? .empty() : flow.rxDismissed.asObservable())
100103
.asSignal(onErrorJustReturn: NoneStep())
101104
.emit(to: self.stepsRelay)

RxFlowTests/FlowCoordinatorTests.swift

+46
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#if canImport(UIKit)
1010

11+
import Dispatch
1112
@testable import RxFlow
1213
import XCTest
1314
import RxBlocking
@@ -367,6 +368,51 @@ final class FlowCoordinatorTests: XCTestCase {
367368

368369
XCTAssertNil(leakingFlowReference)
369370
}
371+
372+
func testNavigate_executes_on_mainThread() {
373+
class ThreadRecorderFlow: Flow {
374+
let rootViewController = UINavigationController()
375+
var root: Presentable {
376+
return rootViewController
377+
}
378+
379+
var recordedThreadName: String?
380+
381+
func adapt(step: Step) -> Single<Step> {
382+
return Single.just(step).observe(on: SerialDispatchQueueScheduler(internalSerialQueueName: UUID().uuidString))
383+
}
384+
385+
func navigate(to step: Step) -> FlowContributors {
386+
self.recordedThreadName = DispatchQueue.currentLabel
387+
return .none
388+
}
389+
}
390+
391+
let exp = expectation(description: "Navigates on main thread")
392+
393+
// Given: a Flow that records its navigation thread (and adapt on a background thread)
394+
let recorderFlow = ThreadRecorderFlow()
395+
let sut = FlowCoordinator()
396+
397+
Flows.use(recorderFlow, when: .ready) { (_) in
398+
exp.fulfill()
399+
}
400+
401+
// When: coordinating that flow
402+
sut.coordinate(flow: recorderFlow,
403+
with: OneStepper(withSingleStep: TestSteps.one))
404+
405+
waitForExpectations(timeout: 0.5)
406+
407+
// Then: the flow navigates on the main thread
408+
XCTAssertEqual(recorderFlow.recordedThreadName, "com.apple.main-thread")
409+
}
410+
}
411+
412+
extension DispatchQueue {
413+
class var currentLabel: String {
414+
return String(validatingUTF8: __dispatch_queue_get_label(nil))!
415+
}
370416
}
371417

372418
#endif

xcarthage-archive.sh

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# carthage-update.sh
2+
# Usage example: ./carthage-archive.sh
3+
4+
set -euo pipefail
5+
6+
xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
7+
trap 'rm -f "$xcconfig"' INT TERM HUP EXIT
8+
9+
# For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise
10+
# the build will fail on lipo due to duplicate architectures.
11+
12+
CURRENT_XCODE_VERSION=$(xcodebuild -version | grep "Build version" | cut -d' ' -f3)
13+
echo "EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$CURRENT_XCODE_VERSION = arm64 arm64e armv7 armv7s armv6 armv8" >> $xcconfig
14+
15+
echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$(XCODE_PRODUCT_BUILD_VERSION))' >> $xcconfig
16+
echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig
17+
18+
export XCODE_XCCONFIG_FILE="$xcconfig"
19+
carthage build --archive

xcarthage-bootstrap.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x8
1616
echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig
1717

1818
export XCODE_XCCONFIG_FILE="$xcconfig"
19-
carthage bootstrap "$@"
19+
carthage bootstrap "$@" --cache-builds

0 commit comments

Comments
 (0)