@@ -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