Integrate高德 SDK with simulator-safe build flags, add map views for operating area and punch points, refactor main tabs and key feature screens to UIKit with Diffable lists, and document Swift concurrency defaults in AGENTS.md. Co-authored-by: Cursor <cursoragent@cursor.com>
150 lines
5.0 KiB
Swift
150 lines
5.0 KiB
Swift
//
|
||
// RootViewController.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import SnapKit
|
||
import UIKit
|
||
|
||
@MainActor
|
||
/// 应用根 ViewController,根据登录阶段切换子页面并挂载全局 Overlay。
|
||
final class RootViewController: UIViewController {
|
||
|
||
private let services: AppServices
|
||
private var currentChild: UIViewController?
|
||
private let restoringView = UIView()
|
||
|
||
/// 初始化实例。
|
||
init(services: AppServices = .shared) {
|
||
self.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
|
||
restoringView.backgroundColor = .systemBackground
|
||
|
||
services.appSession.onChange = { [weak self] in
|
||
self?.handleSessionPhaseChange()
|
||
}
|
||
|
||
mountChild(for: services.appSession.phase)
|
||
bootstrapSession()
|
||
configurePushNotifications()
|
||
}
|
||
|
||
/// 窗口展示后将 Toast 与全局 Loading 挂载到 window 层级。
|
||
override func viewDidAppear(_ animated: Bool) {
|
||
super.viewDidAppear(animated)
|
||
attachGlobalOverlays()
|
||
}
|
||
|
||
/// 冷启动时恢复本地会话,并在已登录时申请推送权限。
|
||
private func bootstrapSession() {
|
||
Task {
|
||
await services.globalLoading.withLoading {
|
||
await services.sessionBootstrapper.restore(
|
||
appSession: services.appSession,
|
||
accountContext: services.accountContext,
|
||
permissionContext: services.permissionContext,
|
||
profileAPI: services.profileAPI,
|
||
accountContextAPI: services.accountContextAPI,
|
||
toastCenter: services.toastCenter
|
||
)
|
||
}
|
||
|
||
if services.appSession.isLoggedIn, !AppUITestLaunchState.isRunningUITests {
|
||
PushNotificationManager.shared.requestAuthorizationAndRegister()
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 配置推送管理器与路由跳转回调。
|
||
private func configurePushNotifications() {
|
||
services.configurePushNotifications()
|
||
}
|
||
|
||
/// 将全局 Toast 与 Loading 视图附加到当前 window。
|
||
private func attachGlobalOverlays() {
|
||
guard let window = view.window else { return }
|
||
services.toastCenter.attachToWindow(window)
|
||
services.globalLoading.attachToWindow(window)
|
||
}
|
||
|
||
/// 登录阶段变化时切换子页面,并在登出时清理各上下文状态。
|
||
private func handleSessionPhaseChange() {
|
||
mountChild(for: services.appSession.phase)
|
||
|
||
switch services.appSession.phase {
|
||
case .loggedIn:
|
||
if !AppUITestLaunchState.isRunningUITests {
|
||
PushNotificationManager.shared.requestAuthorizationAndRegister()
|
||
}
|
||
Task { await reloadScenicSpotContextIfNeeded() }
|
||
case .loggedOut:
|
||
// 登出后重置账号、权限、路由与排队运行时,避免残留旧状态
|
||
services.accountContext.reset()
|
||
services.permissionContext.reset()
|
||
services.scenicSpotContext.reset()
|
||
services.appRouter.reset()
|
||
services.toastCenter.dismiss()
|
||
services.scenicQueueRuntime.stop()
|
||
case .restoring:
|
||
break
|
||
}
|
||
}
|
||
|
||
/// 登录成功后按当前景区 ID 刷新景区上下文。
|
||
private func reloadScenicSpotContextIfNeeded() async {
|
||
guard services.appSession.isLoggedIn else {
|
||
services.scenicSpotContext.reset()
|
||
return
|
||
}
|
||
await services.scenicSpotContext.reload(
|
||
scenicId: services.accountContext.currentScenic?.id,
|
||
api: services.accountContextAPI
|
||
)
|
||
}
|
||
|
||
/// 按登录阶段挂载对应子控制器:登录页、恢复占位或主 Tab。
|
||
private func mountChild(for phase: AuthPhase) {
|
||
let nextChild: UIViewController
|
||
switch phase {
|
||
case .loggedOut:
|
||
nextChild = LoginViewController(services: services)
|
||
case .restoring:
|
||
nextChild = restoringViewController()
|
||
case .loggedIn:
|
||
nextChild = MainTabBarController(services: services)
|
||
}
|
||
|
||
guard currentChild !== nextChild else { return }
|
||
|
||
currentChild?.willMove(toParent: nil)
|
||
currentChild?.view.removeFromSuperview()
|
||
currentChild?.removeFromParent()
|
||
|
||
addChild(nextChild)
|
||
view.addSubview(nextChild.view)
|
||
nextChild.view.snp.makeConstraints { make in
|
||
make.edges.equalToSuperview()
|
||
}
|
||
nextChild.didMove(toParent: self)
|
||
currentChild = nextChild
|
||
}
|
||
|
||
/// 会话恢复阶段的空白占位页,避免闪烁登录页。
|
||
private func restoringViewController() -> UIViewController {
|
||
let controller = UIViewController()
|
||
controller.view.backgroundColor = .systemBackground
|
||
return controller
|
||
}
|
||
}
|