新增 APNs 推送注册与 Payload 路由
引入 AppDelegate、显式 Info.plist 与 entitlements,并将 PushNotificationManager 接入登录生命周期,用于 device token 上报与通知处理。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
41
suixinkan/App/AppDelegate.swift
Normal file
41
suixinkan/App/AppDelegate.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/26.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/// AppDelegate 承接 APNs 系统回调,SwiftUI App 入口通过 adaptor 注入。
|
||||
final class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
|
||||
) {
|
||||
Task { @MainActor in
|
||||
PushNotificationManager.shared.handleDeviceToken(deviceToken)
|
||||
}
|
||||
}
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFailToRegisterForRemoteNotificationsWithError error: Error
|
||||
) {
|
||||
Task { @MainActor in
|
||||
PushNotificationManager.shared.handleRegistrationError(error)
|
||||
}
|
||||
}
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
|
||||
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
|
||||
) {
|
||||
let payload = PushPayload(userInfo: userInfo)
|
||||
Task { @MainActor in
|
||||
PushNotificationManager.shared.handleRemoteNotification(payload)
|
||||
completionHandler(.newData)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,7 @@ struct RootView: View {
|
||||
@State private var statisticsAPI: StatisticsAPI
|
||||
@State private var paymentAPI: PaymentAPI
|
||||
@State private var walletAPI: WalletAPI
|
||||
@State private var pushAPI: PushAPI
|
||||
@State private var scenicPermissionAPI: ScenicPermissionAPI
|
||||
@State private var scenicSettlementAPI: ScenicSettlementAPI
|
||||
@State private var messageCenterAPI: MessageCenterAPI
|
||||
@ -63,6 +64,7 @@ struct RootView: View {
|
||||
_statisticsAPI = State(initialValue: StatisticsAPI(client: apiClient))
|
||||
_paymentAPI = State(initialValue: PaymentAPI(client: apiClient))
|
||||
_walletAPI = State(initialValue: WalletAPI(client: apiClient))
|
||||
_pushAPI = State(initialValue: PushAPI(client: apiClient))
|
||||
_scenicPermissionAPI = State(initialValue: ScenicPermissionAPI(client: apiClient))
|
||||
_scenicSettlementAPI = State(initialValue: ScenicSettlementAPI(client: apiClient))
|
||||
_messageCenterAPI = State(initialValue: MessageCenterAPI(client: apiClient))
|
||||
@ -114,6 +116,7 @@ struct RootView: View {
|
||||
.environment(statisticsAPI)
|
||||
.environment(paymentAPI)
|
||||
.environment(walletAPI)
|
||||
.environment(pushAPI)
|
||||
.environment(scenicPermissionAPI)
|
||||
.environment(scenicSettlementAPI)
|
||||
.environment(messageCenterAPI)
|
||||
@ -132,6 +135,7 @@ struct RootView: View {
|
||||
.environment(authSessionCoordinator)
|
||||
.task {
|
||||
apiClient.bindAuthTokenProvider { appSession.token }
|
||||
PushNotificationManager.shared.configure(api: pushAPI, session: appSession, router: appRouter)
|
||||
await globalLoading.withLoading {
|
||||
await sessionBootstrapper.restore(
|
||||
appSession: appSession,
|
||||
@ -142,6 +146,9 @@ struct RootView: View {
|
||||
toastCenter: toastCenter
|
||||
)
|
||||
}
|
||||
if appSession.isLoggedIn {
|
||||
PushNotificationManager.shared.requestAuthorizationAndRegister()
|
||||
}
|
||||
}
|
||||
.task(id: scenicSpotTaskID) {
|
||||
guard appSession.isLoggedIn else {
|
||||
@ -176,12 +183,18 @@ struct RootView: View {
|
||||
}
|
||||
}
|
||||
.onChange(of: appSession.phase) { _, phase in
|
||||
guard phase == .loggedOut else { return }
|
||||
accountContext.reset()
|
||||
permissionContext.reset()
|
||||
scenicSpotContext.reset()
|
||||
appRouter.reset()
|
||||
toastCenter.dismiss()
|
||||
switch phase {
|
||||
case .loggedIn:
|
||||
PushNotificationManager.shared.requestAuthorizationAndRegister()
|
||||
case .loggedOut:
|
||||
accountContext.reset()
|
||||
permissionContext.reset()
|
||||
scenicSpotContext.reset()
|
||||
appRouter.reset()
|
||||
toastCenter.dismiss()
|
||||
case .restoring:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,8 @@ import SwiftUI
|
||||
/// 应用入口实体,负责把 SwiftUI 根视图挂载到主窗口。
|
||||
@main
|
||||
struct suixinkanApp: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
RootView()
|
||||
|
||||
Reference in New Issue
Block a user