Files
suixinkan_uikit/suixinkan/SceneDelegate.swift
T

238 lines
9.7 KiB
Swift

//
// SceneDelegate.swift
// suixinkan
//
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
private var sessionExpiredDialog: SessionExpiredDialogViewController?
private var deregistrationCoordinator: StoreAccountDeregistrationRootCoordinator?
private var needsForegroundDeregistrationCheck = false
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
AppNavigationBarAppearance.applyGlobalAppearance()
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIViewController()
self.window = window
(UIApplication.shared.delegate as? AppDelegate)?.window = window
let appSession = AppStore.shared.session
deregistrationCoordinator = StoreAccountDeregistrationRootCoordinator(
window: window, session: appSession,
makeAPI: { identity in
StoreAccountDeregistrationAPI(client: NetworkServices.shared.apiClient, identity: identity) {
identity.matches(session: appSession)
}
},
makeSubmissionStore: { StoreAccountDeregistrationSubmissionStore(storeUserID: $0) },
makeBusinessRoot: { MainTabBarController() },
setBindingSuspended: { PushNotificationManager.shared.setAccountBindingSuspended($0) },
onBusinessResumed: {
PushNotificationManager.shared.handleLoginCompleted()
PushNotificationManager.shared.routePendingNotificationIfPossible()
}
)
PushNotificationManager.shared.attach(window: window)
registerNotifications()
refreshRootForCurrentSession()
window.makeKeyAndVisible()
if let response = connectionOptions.notificationResponse {
PushNotificationManager.shared.handleNotificationResponse(response)
}
}
func sceneDidDisconnect(_ scene: UIScene) {
deregistrationCoordinator?.cancelPendingCheck()
deregistrationCoordinator = nil
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<UIOpenURLContext>) {
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) {
guard accessController == nil, deregistrationCoordinator?.isChecking != true else { return }
PushNotificationManager.shared.retryPendingRegistrationUpload()
}
func sceneDidEnterBackground(_ scene: UIScene) {
needsForegroundDeregistrationCheck = true
}
func sceneWillEnterForeground(_ scene: UIScene) {
guard needsForegroundDeregistrationCheck else { return }
needsForegroundDeregistrationCheck = false
guard sessionExpiredDialog == nil else { return }
deregistrationCoordinator?.resumeFromBackground()
}
private func registerNotifications() {
NotificationCenter.default.addObserver(
self, selector: #selector(handleStoreDeregistrationRestriction(_:)),
name: NotificationName.storeAccountDeregistrationRestricted, object: nil
)
NotificationCenter.default.addObserver(
self, selector: #selector(handleStoreDeregistrationSubmitted(_:)),
name: NotificationName.storeAccountDeregistrationSubmitted, object: nil
)
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
)
}
@objc private func handleSessionDidExpire() {
guard AppStore.shared.session.isLoggedIn else { return }
guard sessionExpiredDialog == nil else { return }
deregistrationCoordinator?.cancelPendingCheck()
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() {
clearAuthenticatedSession()
AppRouter.setRoot(.login, on: window)
}
/// 统一清除当前会话及依赖登录态的后台能力,不执行页面跳转。
private func clearAuthenticatedSession() {
deregistrationCoordinator?.cancelPendingCheck()
sessionExpiredDialog?.dismiss(animated: false)
sessionExpiredDialog = nil
GlobalLoadingManager.shared.hideAll()
PushNotificationManager.shared.handleLogout()
PushNotificationManager.shared.setAccountBindingSuspended(false)
AppStore.shared.logout()
}
@objc private func handleUserDidLogout() {
transitionToLogin()
}
/// 申请明确受理后先退出,再展示只含“完成”的结果页;结果页不再依赖已清除的凭证。
@objc private func handleStoreDeregistrationSubmitted(_ notification: Notification) {
let identityName = notification.userInfo?[NotificationUserInfoKey.deregistrationIdentityName] as? String
?? "当前门店身份"
let coolingUntil = notification.userInfo?[NotificationUserInfoKey.deregistrationCoolingUntil] as? String
clearAuthenticatedSession()
guard !AppStore.shared.session.isLoggedIn else {
AppRouter.setRoot(.login, on: window)
return
}
let controller = StoreAccountDeregistrationSubmittedViewController(
identityName: identityName,
coolingUntil: coolingUntil
) { [weak self] in
AppRouter.setRoot(.login, on: self?.window)
}
AppRouter.setRoot(UINavigationController(rootViewController: controller), on: window)
}
@objc private func handleUserDidLogin() {
sessionExpiredDialog?.dismiss(animated: false)
sessionExpiredDialog = nil
// v9 登录响应中的 store_users[].status 是登录是否需要注销确认的唯一依据。
// 旧 account-deregister/status 仅供用户主动进入注销设置流程时查询,不能再拦截正常登录。
refreshRootForCurrentSession()
}
@objc private func handleAccountDidSwitch() {
if AppStore.shared.session.accountType == .storeUser {
refreshRootForCurrentSession()
} else {
deregistrationCoordinator?.cancelPendingCheck()
PushNotificationManager.shared.setAccountBindingSuspended(false)
PushNotificationManager.shared.handleAccountSwitched()
}
}
/// 仅检查真实根控制器,避免被旧的异步查询或页面导航状态误判。
private var accessController: StoreAccountDeregistrationAccessViewController? {
deregistrationCoordinator?.accessController
}
/// 冷启动和切换身份使用已有会话,不主动查询注销状态;新登录由独立入口核验。
private func refreshRootForCurrentSession() {
let session = AppStore.shared.session
guard session.isLoggedIn else {
deregistrationCoordinator?.cancelPendingCheck()
PushNotificationManager.shared.setAccountBindingSuspended(false)
AppRouter.setRoot(.login, on: window, animated: false)
return
}
deregistrationCoordinator?.restoreSession()
}
/// 150015 只限制原请求对应的当前门店身份;旧 Token 响应不会影响新身份。
@objc private func handleStoreDeregistrationRestriction(_ notification: Notification) {
let token = notification.userInfo?[NotificationUserInfoKey.deregistrationRequestToken] as? String
let session = AppStore.shared.session
guard StoreAccountDeregistrationAccessViewModel.restrictionApplies(requestToken: token, session: session) else { return }
deregistrationCoordinator?.recordRestriction(requestToken: token)
}
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
}
}