// // SceneDelegate.swift // suixinkan // import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? private var sessionExpiredDialog: SessionExpiredDialogViewController? func scene( _ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions ) { guard let windowScene = scene as? UIWindowScene else { return } AppNavigationBarAppearance.applyGlobalAppearance() let debugPreviewRoot = makeAccountDeletionDebugPreviewRootIfNeeded() if debugPreviewRoot == nil { enforceAccountDeletionStateBeforeLaunch() } let window = UIWindow(windowScene: windowScene) window.rootViewController = debugPreviewRoot ?? AppRouter.makeRootViewController() window.makeKeyAndVisible() self.window = window (UIApplication.shared.delegate as? AppDelegate)?.window = window PushNotificationManager.shared.attach(window: window) registerNotifications() if AppStore.shared.session.isLoggedIn { DispatchQueue.main.async { PushNotificationManager.shared.handleLoginCompleted() } } if let response = connectionOptions.notificationResponse { PushNotificationManager.shared.handleNotificationResponse(response) } } func sceneDidDisconnect(_ scene: UIScene) { NotificationCenter.default.removeObserver(self) if (UIApplication.shared.delegate as? AppDelegate)?.window === window { (UIApplication.shared.delegate as? AppDelegate)?.window = nil } } func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { guard let url = URLContexts.first?.url else { return } _ = WeChatManager.shared.handleOpenURL(url) } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { _ = WeChatManager.shared.handleContinueUserActivity(userActivity) } func sceneDidBecomeActive(_ scene: UIScene) { PushNotificationManager.shared.retryPendingRegistrationUpload() } private func registerNotifications() { NotificationCenter.default.addObserver( self, selector: #selector(handleSessionDidExpire), name: NotificationName.sessionDidExpire, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(handleUserDidLogout), name: NotificationName.userDidLogout, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(handleUserDidLogin), name: NotificationName.userDidLogin, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(handleAccountDidSwitch), name: NotificationName.accountDidSwitch, object: nil ) } /// 为设计核对提供仅 Debug 生效的注销页直达入口,不影响正式启动流程。 private func makeAccountDeletionDebugPreviewRootIfNeeded() -> UIViewController? { #if DEBUG guard ProcessInfo.processInfo.arguments.contains("--account-deletion-preview") else { return nil } let service = AccountDeletionMockService(responseDelayNanoseconds: 0) let viewModel = AccountDeletionViewModel(username: "18651857230", service: service) return UINavigationController(rootViewController: AccountDeletionViewController(viewModel: viewModel)) #else return nil #endif } /// App 冷启动时若账号已进入注销冷静期,则清除旧登录态并返回登录页。 private func enforceAccountDeletionStateBeforeLaunch() { guard AppStore.shared.session.isLoggedIn, let username = AccountDeletionIdentity.currentUsername() else { return } switch AccountDeletionMockService.shared.loginState(for: username) { case .pending, .completed: AppStore.shared.logout() case .none: break } } @objc private func handleSessionDidExpire() { guard AppStore.shared.session.isLoggedIn else { return } guard sessionExpiredDialog == nil else { return } GlobalLoadingManager.shared.hideAll() let dialog = SessionExpiredDialogViewController { [weak self] in self?.transitionToLogin() } sessionExpiredDialog = dialog guard let presenter = topViewController(from: window?.rootViewController) else { sessionExpiredDialog = nil return } presenter.present(dialog, animated: true) } private func transitionToLogin() { sessionExpiredDialog = nil PushNotificationManager.shared.handleLogout() AppStore.shared.logout() AppRouter.setRoot(.login, on: window) } @objc private func handleUserDidLogout() { transitionToLogin() } @objc private func handleUserDidLogin() { sessionExpiredDialog?.dismiss(animated: false) sessionExpiredDialog = nil AppRouter.setRoot(.mainTab, on: window) DispatchQueue.main.async { PushNotificationManager.shared.handleLoginCompleted() PushNotificationManager.shared.routePendingNotificationIfPossible() } } @objc private func handleAccountDidSwitch() { PushNotificationManager.shared.handleAccountSwitched() } private func topViewController(from viewController: UIViewController?) -> UIViewController? { guard let viewController else { return nil } if let presented = viewController.presentedViewController { return topViewController(from: presented) } if let navigationController = viewController as? UINavigationController { return topViewController(from: navigationController.visibleViewController) } if let tabBarController = viewController as? UITabBarController { return topViewController(from: tabBarController.selectedViewController) } return viewController } }