55 lines
2.1 KiB
Swift
55 lines
2.1 KiB
Swift
import UserNotifications
|
|
|
|
/// Notification Service Extension — runs as a separate process even when the main app
|
|
/// is terminated. Intercepts push notifications with `mutable-content: 1` and:
|
|
/// 1. Adds `.default` sound for vibration (server payload has no sound)
|
|
/// 2. Increments the app icon badge from shared App Group storage
|
|
final class NotificationService: UNNotificationServiceExtension {
|
|
|
|
private static let appGroupID = "group.com.rosetta.dev"
|
|
private static let badgeKey = "app_badge_count"
|
|
|
|
private var contentHandler: ((UNNotificationContent) -> Void)?
|
|
private var bestAttemptContent: UNMutableNotificationContent?
|
|
|
|
override func didReceive(
|
|
_ request: UNNotificationRequest,
|
|
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
|
|
) {
|
|
self.contentHandler = contentHandler
|
|
bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
|
|
|
|
guard let content = bestAttemptContent else {
|
|
contentHandler(request.content)
|
|
return
|
|
}
|
|
|
|
// 1. Add sound for vibration — server APNs payload has no sound field.
|
|
content.sound = .default
|
|
|
|
// 2. Increment badge count from shared App Group storage.
|
|
if let shared = UserDefaults(suiteName: Self.appGroupID) {
|
|
let current = shared.integer(forKey: Self.badgeKey)
|
|
let newBadge = current + 1
|
|
shared.set(newBadge, forKey: Self.badgeKey)
|
|
content.badge = NSNumber(value: newBadge)
|
|
}
|
|
|
|
// 3. Ensure notification category for CarPlay parity.
|
|
if content.categoryIdentifier.isEmpty {
|
|
content.categoryIdentifier = "message"
|
|
}
|
|
|
|
contentHandler(content)
|
|
}
|
|
|
|
/// Called if the extension takes too long (30s limit).
|
|
/// Deliver the best attempt content with at least the sound set.
|
|
override func serviceExtensionTimeWillExpire() {
|
|
if let handler = contentHandler, let content = bestAttemptContent {
|
|
content.sound = .default
|
|
handler(content)
|
|
}
|
|
}
|
|
}
|