KasztelanKarta/Kasztelan/NotificationService.swift

72 lines
3.2 KiB
Swift

import Foundation
import UserNotifications
import UIKit
enum NotificationService {
@MainActor
static func requestPermission() async {
_ = try? await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge])
UIApplication.shared.registerForRemoteNotifications()
}
@MainActor private static var deviceToken: String?
@MainActor private static var registeredSession: String?
@MainActor
static func receivedDeviceToken(_ data: Data) async {
deviceToken = data.map { String(format: "%02x", $0) }.joined()
registeredSession = nil
await syncPushRegistration()
}
@MainActor
static func syncPushRegistration() async {
let session = KeychainStore.load()
guard !session.isEmpty, let deviceToken, registeredSession != session else { return }
let environment = Bundle.main.object(forInfoDictionaryKey: "KASZTELANPushEnvironment") as? String ?? "production"
do {
try await APIClient().registerPush(deviceToken: deviceToken, environment: environment, token: session)
// The account may have changed while the request was in flight.
if KeychainStore.load() == session { registeredSession = session }
} catch {
// Retry when the app next refreshes; keep account access working offline.
}
}
@MainActor
static func unregisterPush(session: String) async {
registeredSession = nil
guard let deviceToken else { return }
try? await APIClient().unregisterPush(deviceToken: deviceToken, token: session)
}
static func recordReceivedPush(_ content: UNNotificationContent) {
guard let id = content.userInfo["message_id"] as? Int, id > 0 else { return }
var received = UserDefaults.standard.array(forKey: "kasztelan.push.receivedIDs") as? [Int] ?? []
received.removeAll { $0 == id }
received.append(id)
UserDefaults.standard.set(Array(received.suffix(100)), forKey: "kasztelan.push.receivedIDs")
}
static func show(_ message: KasztelanMessage) async {
let received = UserDefaults.standard.array(forKey: "kasztelan.push.receivedIDs") as? [Int] ?? []
guard !received.contains(message.id) else { return }
let settings = await UNUserNotificationCenter.current().notificationSettings()
guard settings.authorizationStatus == .authorized || settings.authorizationStatus == .provisional else { return }
let delivered = await UNUserNotificationCenter.current().deliveredNotifications()
guard !delivered.contains(where: {
($0.request.content.userInfo["message_id"] as? Int) == message.id
}) else { return }
let content = UNMutableNotificationContent()
content.title = "KASZTELAN"
content.subtitle = message.title
content.body = message.text
content.sound = .default
content.threadIdentifier = "kasztelan-messages"
content.categoryIdentifier = "KASZTELAN_MESSAGE"
content.userInfo = ["message_id": message.id]
let request = UNNotificationRequest(identifier: "kasztelan-\(message.id)", content: content, trigger: nil)
try? await UNUserNotificationCenter.current().add(request)
}
}