diff options
| author | Christian Cleberg <[email protected]> | 2026-04-21 21:53:16 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-21 21:53:16 -0500 |
| commit | 1f58092dcb67cded75a850db364f3b46d86181f3 (patch) | |
| tree | 662731251ed1022b4a1585ec74379f233ca6705a /DomainDig/LocalNotificationService.swift | |
| parent | 2e90ee4ceca73d5126118920b486149cb227c4e9 (diff) | |
| download | domain-dig-1f58092dcb67cded75a850db364f3b46d86181f3.tar.gz domain-dig-1f58092dcb67cded75a850db364f3b46d86181f3.tar.bz2 domain-dig-1f58092dcb67cded75a850db364f3b46d86181f3.zip | |
feat(v2.2.0): add foreground monitoring, notifications, and smarter diffs
- implement “Check All” sweep for tracked domains
- add local notifications for changes and certificate expiration
- introduce change severity filtering (low/medium/high)
- enhance change summaries and diff readability
- add certificate expiration tracking and alerts
- improve watchlist indicators for changes
- add sweep summary screen
- optimize performance for larger watchlists
Diffstat (limited to 'DomainDig/LocalNotificationService.swift')
| -rw-r--r-- | DomainDig/LocalNotificationService.swift | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/DomainDig/LocalNotificationService.swift b/DomainDig/LocalNotificationService.swift new file mode 100644 index 0000000..450d844 --- /dev/null +++ b/DomainDig/LocalNotificationService.swift @@ -0,0 +1,89 @@ +import Foundation +import UserNotifications + +@MainActor +final class LocalNotificationService { + static let shared = LocalNotificationService() + + private init() {} + + func configureForegroundPresentation() { + UNUserNotificationCenter.current().delegate = NotificationCenterDelegate.shared + } + + func requestAuthorizationIfNeeded() async -> Bool { + let center = UNUserNotificationCenter.current() + let settings = await center.notificationSettings() + + switch settings.authorizationStatus { + case .authorized, .provisional, .ephemeral: + return true + case .notDetermined: + return (try? await center.requestAuthorization(options: [.alert, .badge, .sound])) ?? false + case .denied: + return false + @unknown default: + return false + } + } + + func notifyDomainEvent(domain: String, message: String, severity: ChangeSeverity) async { + await schedule( + identifier: "domain-change-\(domain)", + title: domain, + body: message, + interruptionLevel: severity == .high ? .timeSensitive : .active + ) + } + + func notifyCertificateWarning(domain: String, daysRemaining: Int) async { + await schedule( + identifier: "cert-warning-\(domain)", + title: domain, + body: "Certificate expires in \(daysRemaining) days", + interruptionLevel: .timeSensitive + ) + } + + func notifySweepComplete(summary: BatchSweepSummary) async { + let body = "\(summary.changedDomains) changed, \(summary.warningDomains) warnings, \(summary.unchangedDomains) unchanged" + await schedule( + identifier: "sweep-complete", + title: summary.source == .watchlistRefresh ? "Check All Complete" : "Batch Complete", + body: body, + interruptionLevel: .active + ) + } + + private func schedule( + identifier: String, + title: String, + body: String, + interruptionLevel: UNNotificationInterruptionLevel + ) async { + let content = UNMutableNotificationContent() + content.title = title + content.body = body + content.sound = .default + content.interruptionLevel = interruptionLevel + + let request = UNNotificationRequest( + identifier: identifier, + content: content, + trigger: UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false) + ) + + try? await UNUserNotificationCenter.current().add(request) + } +} + +private final class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate { + static let shared = NotificationCenterDelegate() + + func userNotificationCenter( + _ center: UNUserNotificationCenter, + willPresent notification: UNNotification + ) async -> UNNotificationPresentationOptions { + [.banner, .list, .sound] + } +} |
