WatchAppDelegate.swift 4.02 KB
//
//  WatchAppDelegate.swift
//  Runner Watch App
//
//  Created by 权海 on 2026/6/17.
//

import WatchKit
import UserNotifications

@MainActor
final class WatchAppDelegate: NSObject, WKApplicationDelegate, UNUserNotificationCenterDelegate {
    private enum HealthAuthorizationState {
        case notRequested
        case requesting
        case completed
    }

    private var healthAuthorizationState: HealthAuthorizationState = .notRequested

    func applicationDidFinishLaunching() {
        setup()
        let center = UNUserNotificationCenter.current()
        center.delegate = self
        requestNotificationPermission()
    }

    private func setup(){
        WatchUserAgent.share.genUA()
        WatchSessionManager.share.addListen()
        WatchUserinfoManager.share.setup()
        requestHealthAuthorizationIfNeeded()
    }

    func sceneDidBecomeActive() {
        switch healthAuthorizationState {
        case .completed:
            // Local HealthKit reads and observer registration never depend on
            // login state. Upload methods perform their own token check.
            WatchHealthWidgetRefreshCoordinator.shared.refreshAll()
            WatchHealthObserverUploader.shared.startObserversIfNeeded()
            WatchHealthObserverUploader.shared.performFullUpload()
        case .notRequested:
            requestHealthAuthorizationIfNeeded()
        case .requesting:
            // The completion below will refresh data and start observers.
            break
        }
    }

    private func requestHealthAuthorizationIfNeeded() {
        guard case .notRequested = healthAuthorizationState else { return }
        healthAuthorizationState = .requesting
        WatchDataManager.share.requestAuthForHealth { [weak self] success in
            guard let self else { return }
            if !success {
                self.healthAuthorizationState = .notRequested
                return
            }

            self.healthAuthorizationState = .completed
            WatchDataManager.share.fetchMyTodayData()
            WatchDataManager.share.getLatestHealthData()
            WatchDataManager.share.getAcitivtyTarget()
            // Register observers only after the authorization request has
            // completed. Registering earlier can make background delivery fail
            // permanently for this process.
            WatchHealthObserverUploader.shared.startObserversIfNeeded()
            WatchHealthObserverUploader.shared.performFullUpload()
        }
    }

    private func requestNotificationPermission() {
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert, .sound, .badge]
        ) { granted, error in
            guard granted else { return }
            DispatchQueue.main.async {
                WKApplication.shared().registerForRemoteNotifications()
            }
        }
    }

    func didRegisterForRemoteNotifications(withDeviceToken deviceToken: Data) {
        let token = deviceToken.map { String(format: "%02x", $0) }.joined()
        AppGroupConstants.defaults?.set(token, forKey: AppGroupConstants.Key.watchDeviceToken)

        Task{
            await UserInfoService().uploadDeviceInfo()
        }
    }

    func didFailToRegisterForRemoteNotificationsWithError(_ error: Error) {

    }

    // App 在前台收到通知:热启动前台展示
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification
    ) async -> UNNotificationPresentationOptions {
        let userInfo = notification.request.content.userInfo
        print("watch will present:", userInfo)

        return [.banner, .sound]
    }

    // 用户点击通知:冷启动 / 后台 / 前台都走这里
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse
    ) async {
        let userInfo = response.notification.request.content.userInfo
        print("watch notification clicked:", userInfo)

        WatchNotificationRouter.shared.handle(userInfo: userInfo)
    }
}