240 lines
9.7 KiB
Swift
240 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
|
|
if AppStore.shared.session.isLoggedIn, AppStore.shared.session.accountType == .storeUser {
|
|
deregistrationCoordinator?.check()
|
|
} else {
|
|
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
|
|
}
|
|
}
|