如何在App前台活跃时全局显示异地登录登出提示弹窗?
解决App前台显示异地登录弹窗的方案
核心思路
不用在每个ViewController里重复编写检查逻辑,而是借助AppDelegate的生命周期回调触发登录状态校验,再通过全局工具方法获取当前顶层ViewController来展示弹窗,实现一次编写、全局生效。
具体实现步骤
1. 利用App活跃回调触发状态检查
重写applicationDidBecomeActive方法,每次App从后台回到前台或刚启动进入活跃状态时,自动执行账号登录状态检查:
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { // 防止重复发起API请求的标志位 private var isCheckingLoginStatus = false func applicationDidBecomeActive(_ application: UIApplication) { checkAccountLoginStatus() } // 封装登录状态检查逻辑 private func checkAccountLoginStatus() { guard !isCheckingLoginStatus, !AuthManager.shared.userLoginSession.isEmpty, let tokenlogin = SettingsManager.tokenlogin, !tokenlogin.isEmpty, let fcmToken = Messaging.messaging().fcmToken else { return } isCheckingLoginStatus = true NetworkManager.instance.requestObject(ServiceAPI.start(regID: fcmToken, id: AuthManager.shared.userLoginSession, token: tokenlogin), c: SingleResponse<StartResponse>.self) { [weak self] result in guard let self = self else { return } self.isCheckingLoginStatus = false switch result { case .success(let response): AuthManager.shared.userLoginSession = response.data.apikey ?? "" case .failure(let error): // 检测到异地登录,显示弹窗并执行登出 self.show异地登录弹窗(message: error.description) } } } }
2. 实现全局弹窗与登出逻辑
在AppDelegate中添加获取顶层ViewController的方法,解决无法直接弹窗的问题,同时封装弹窗和登出逻辑:
extension AppDelegate { // 获取当前显示的顶层ViewController private func 获取顶层ViewController() -> UIViewController? { var topVC = UIApplication.shared.keyWindow?.rootViewController while let presentedVC = topVC?.presentedViewController { topVC = presentedVC } return topVC } // 显示异地登录提示弹窗 private func show异地登录弹窗(message: String) { DispatchQueue.main.async { [weak self] in guard let self = self, let topVC = self.获取顶层ViewController() else { return } let alert = UIAlertController( title: "账号登录提示", message: "您的账号已在其他设备登录,当前设备将被登出。\n错误信息:\(message)", preferredStyle: .alert ) alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in self.执行登出逻辑() }) topVC.present(alert, animated: true) } } // 执行登出后的状态清理与页面跳转 private func 执行登出逻辑() { // 清空登录状态 AuthManager.shared.userLoginSession = "" SettingsManager.tokenlogin = nil // 跳转到登录页面(替换成你的登录ViewController) let loginVC = LoginViewController() let navVC = UINavigationController(rootViewController: loginVC) navVC.modalPresentationStyle = .fullScreen if let topVC = 获取顶层ViewController() { topVC.present(navVC, animated: true) } } }
3. 保留FCM Token更新时的检查
在原有的MessagingDelegate扩展中,添加App活跃状态下的检查触发,确保Token更新时也能验证登录状态:
extension AppDelegate: MessagingDelegate { func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { guard let fcmToken = fcmToken else { return } print("Firebase registration token: \(fcmToken)") let dataDict:[String: String] = ["token": fcmToken] NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict) // 如果当前App处于活跃状态,立即触发一次登录检查 if UIApplication.shared.applicationState == .active { checkAccountLoginStatus() } } }
关键注意事项
- 避免重复请求:加入
isCheckingLoginStatus标志位,防止App频繁切换活跃状态时重复发起API请求。 - 主线程更新UI:弹窗逻辑必须放在主线程执行,所以用
DispatchQueue.main.async包裹。 - 多场景适配:如果项目使用SceneDelegate,需要调整
获取顶层ViewController方法,改用UIApplication.shared.connectedScenes来获取当前窗口。
内容的提问来源于stack exchange,提问作者lauwis
相关产品推荐
相关产品推荐

