Extract dependency wiring and session lifecycle from the container and RootViewController, split NetworkBundle by domain, and add module-level feature services while keeping flat accessors for compatibility. Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
4.3 KiB
Swift
133 lines
4.3 KiB
Swift
//
|
||
// 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)
|
||
}
|
||
}
|