Skip to content

Commit 98f43e4

Browse files
committed
feat: add animation and setup flag
1 parent 9e18ef4 commit 98f43e4

File tree

1 file changed

+33
-12
lines changed

1 file changed

+33
-12
lines changed

FRW/Services/Router/Coordinator.swift

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,31 @@ final class Coordinator {
6363
lazy var rootNavi: UINavigationController? = nil
6464

6565
func showRootView() {
66+
// Guard against multiple calls
67+
guard !isRootViewSetup else {
68+
log.warning("[Coordinator] showRootView() called multiple times, ignoring")
69+
return
70+
}
71+
isRootViewSetup = true
72+
6673
// Set initial root view controller synchronously to avoid launch error
67-
updateRootView(isEmpty: ProfileManager.shared.profiles.isEmpty)
74+
updateRootView(isEmpty: ProfileManager.shared.profiles.isEmpty, animated: false)
6875

6976
// Subscribe to profile changes for dynamic updates
7077
ProfileManager.shared.$profiles
7178
.dropFirst() // Skip initial value since we already handled it above
7279
.receive(on: DispatchQueue.main)
7380
.sink { [weak self] profiles in
74-
self?.updateRootView(isEmpty: profiles.isEmpty)
81+
self?.updateRootView(isEmpty: profiles.isEmpty, animated: true)
7582
}
7683
.store(in: &cancelSets)
7784
}
7885

7986
// MARK: Private
8087

88+
/// Guard against multiple showRootView() calls
89+
private var isRootViewSetup = false
90+
8191
/// Track current root state to avoid redundant updates
8292
private enum RootState {
8393
case reactNative
@@ -86,30 +96,41 @@ final class Coordinator {
8696

8797
private var currentRootState: RootState?
8898

89-
private func updateRootView(isEmpty: Bool) {
99+
private func updateRootView(isEmpty: Bool, animated: Bool) {
90100
let targetState: RootState = isEmpty ? .reactNative : .sideContainer
91101

92102
// Avoid redundant updates
93103
guard currentRootState != targetState else { return }
94104
currentRootState = targetState
95105

106+
let newRootNavi: RouterNavigationController
107+
96108
if isEmpty {
97109
log.info("[Coordinator] profiles empty → show ReactNativeViewController")
98110
let vc = ReactNativeViewController()
99111
vc.route = .getStarted
100-
let navi = RouterNavigationController(rootViewController: vc)
101-
navi.setNavigationBarHidden(true, animated: true)
102-
navi.interactivePopGestureRecognizer?.isEnabled = false
103-
rootNavi = navi
104-
window.rootViewController = rootNavi
112+
newRootNavi = RouterNavigationController(rootViewController: vc)
113+
newRootNavi.setNavigationBarHidden(true, animated: false)
114+
newRootNavi.interactivePopGestureRecognizer?.isEnabled = false
105115
} else {
106116
log.info("[Coordinator] profiles exist → show SideContainerView")
107117
let rootView = SideContainerView()
108118
let hostingView = UIHostingController(rootView: rootView)
109-
let navi = RouterNavigationController(rootViewController: hostingView)
110-
navi.setNavigationBarHidden(true, animated: true)
111-
rootNavi = navi
112-
window.rootViewController = rootNavi
119+
newRootNavi = RouterNavigationController(rootViewController: hostingView)
120+
newRootNavi.setNavigationBarHidden(true, animated: false)
121+
}
122+
123+
rootNavi = newRootNavi
124+
125+
if animated {
126+
// Smooth transition for dynamic updates
127+
newRootNavi.view.alpha = 0
128+
window.rootViewController = newRootNavi
129+
UIView.animate(withDuration: 0.3) {
130+
newRootNavi.view.alpha = 1
131+
}
132+
} else {
133+
window.rootViewController = newRootNavi
113134
}
114135
}
115136

0 commit comments

Comments
 (0)