// // RootViewController.swift // suixinkan // import SnapKit import UIKit @MainActor /// 应用根协调器,监听登录阶段并在 window 根层级切换登录页与 `MainTabBarController`。 final class RootViewController: UIViewController { private let services: AppServices private let lifecycleCoordinator: AppSessionLifecycleCoordinator private var currentAuthChild: UIViewController? /// 初始化实例。 init(services: AppServices = .shared) { self.services = services lifecycleCoordinator = AppSessionLifecycleCoordinator(services: services) super.init(nibName: nil, bundle: nil) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } /// 初始化视图、监听登录阶段变化并启动会话恢复。 override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground services.appSession.onChange = { [weak self] in self?.handleSessionPhaseChange() } applyRoot(for: services.appSession.phase) lifecycleCoordinator.bootstrapSession() lifecycleCoordinator.configurePushNotifications() } /// 登录阶段变化时切换 window 根控制器,并在登出时清理各上下文状态。 private func handleSessionPhaseChange() { applyRoot(for: services.appSession.phase) lifecycleCoordinator.handleSessionPhaseChange() } /// 按登录阶段切换 window 根控制器。 private func applyRoot(for phase: AuthPhase) { switch phase { case .loggedIn: showMainTabBarAsWindowRoot() case .loggedOut: showAuthRoot(with: LoginViewController(services: services)) case .restoring: showAuthRoot(with: restoringViewController()) } } /// 登录后将 `MainTabBarController` 设为 window 根控制器。 private func showMainTabBarAsWindowRoot() { removeCurrentAuthChild() guard let window = activeWindow else { return } let mainTabBarController = MainTabBarController(services: services) window.rootViewController = mainTabBarController attachGlobalOverlays(to: window) } /// 未登录或恢复阶段展示登录/占位页,由当前协调器承载。 private func showAuthRoot(with child: UIViewController) { guard let window = activeWindow else { mountAuthChild(child) return } if window.rootViewController !== self { window.rootViewController = self } mountAuthChild(child) attachGlobalOverlays(to: window) } /// 将登录页或恢复占位页挂载为当前协调器的子控制器。 private func mountAuthChild(_ child: UIViewController) { guard currentAuthChild !== child else { return } currentAuthChild?.willMove(toParent: nil) currentAuthChild?.view.removeFromSuperview() currentAuthChild?.removeFromParent() addChild(child) view.addSubview(child.view) child.view.snp.makeConstraints { make in make.edges.equalToSuperview() } child.didMove(toParent: self) currentAuthChild = child } /// 移除当前认证阶段子控制器。 private func removeCurrentAuthChild() { currentAuthChild?.willMove(toParent: nil) currentAuthChild?.view.removeFromSuperview() currentAuthChild?.removeFromParent() currentAuthChild = nil } /// 会话恢复阶段的空白占位页,避免闪烁登录页。 private func restoringViewController() -> UIViewController { let controller = UIViewController() controller.view.backgroundColor = .systemBackground return controller } /// 将全局 Toast 与 Loading 视图附加到指定 window。 private func attachGlobalOverlays(to window: UIWindow) { services.toastCenter.attachToWindow(window) services.globalLoading.attachToWindow(window) } /// 获取当前 key window。 private var activeWindow: UIWindow? { view.window ?? UIApplication.shared.connectedScenes .compactMap { $0 as? UIWindowScene } .flatMap(\.windows) .first(where: \.isKeyWindow) } }