import Foundation import UserNotifications #if canImport(UIKit) import UIKit #endif public enum PushVendor: String { case apns = "APNS" case fcm = "FCM" } @MainActor public final class PushSDK: NSObject, UNUserNotificationCenterDelegate { public static let shared = PushSDK() private override init() { super.init() } @MainActor public func requestAuthorization(options: UNAuthorizationOptions = [.alert, .badge, .sound]) async throws -> Bool { let granted = try await UNUserNotificationCenter.current().requestAuthorization(options: options) #if canImport(UIKit) if granted { await MainActor.run { UIApplication.shared.registerForRemoteNotifications() } } #endif return granted } public func registerDeviceToken(_ deviceToken: Data, userId: String) async throws { let token = deviceToken.map { String(format: "%02x", $0) }.joined() try await registerToken(token, userId: userId) } public func registerToken(_ token: String, userId: String, vendor: PushVendor = .apns) async throws { let config = XuqmSDK.shared.requireConfig() let _: EmptyResponse = try await ApiClient.shared.request( path: "/api/push/register", method: "POST", queryItems: [ URLQueryItem(name: "appId", value: config.appId), URLQueryItem(name: "userId", value: userId), URLQueryItem(name: "vendor", value: vendor.rawValue), URLQueryItem(name: "token", value: token), ] ) } public func registerFcmToken(_ token: String, userId: String) async throws { try await registerToken(token, userId: userId, vendor: .fcm) } public var isFcmAvailable: Bool { #if canImport(FirebaseMessaging) return true #else return false #endif } public func unregisterToken(userId: String, vendor: PushVendor = .apns) async throws { let config = XuqmSDK.shared.requireConfig() let _: EmptyResponse = try await ApiClient.shared.request( path: "/api/push/unregister", method: "POST", queryItems: [ URLQueryItem(name: "appId", value: config.appId), URLQueryItem(name: "userId", value: userId), URLQueryItem(name: "vendor", value: vendor.rawValue), ] ) } public func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { completionHandler([.banner, .sound, .badge]) } public func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { completionHandler() } }