import UIKit import UserNotifications // APNs 设备令牌的交汇点:解耦 AppDelegate(经 UIKit 回调拿到令牌)与 EngineController(持 // 新鲜会话 token,经引擎桥把令牌上报后端 POST /api/push/apns/register)。令牌走引擎桥注册 // ——与剪贴板 / 吊销同范式,避免原生直连用陈旧 token。 // // 真机 / 账号门控:真正收到推送需 Push Notifications 能力(aps-environment entitlement;真机 // 须付费 ADP 配 profile)与后端 .p8。模拟器(iOS 16+ / Apple Silicon)可注册取令牌、并经 // `simctl push` 验原生处理;真发真测随付费账号落地。 @MainActor final class PushRegistry: NSObject { static let shared = PushRegistry() private override init() { super.init() } weak var engine: EngineController? private(set) var deviceTokenHex: String? private var requested = false // 登录后调用:请求通知授权,授权通过即注册远程通知(触发 AppDelegate didRegister 回调)。 // 幂等——重复调用只在首次生效。 func requestAuthorizationAndRegister() { guard !requested else { return } requested = true UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in guard granted else { return } Task { @MainActor in UIApplication.shared.registerForRemoteNotifications() } } } // AppDelegate 回调(主线程):APNs 下发设备令牌(Data)→ 十六进制 → 经引擎桥上报。 func didRegister(tokenData: Data) { deviceTokenHex = tokenData.map { String(format: "%02x", $0) }.joined() engine?.flushPushRegistration() } // 登出复位:清状态,允许下次登录重新请求授权 / 注册(令牌按会话归属)。 func reset() { requested = false deviceTokenHex = nil } } extension PushRegistry: UNUserNotificationCenterDelegate { // 前台收到推送也展示横幅 + 声音(系统默认前台不展示),与「收到文件」即时提示一致。 func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification ) async -> UNNotificationPresentationOptions { return [ .banner, .sound ] } }