import UserNotifications import Foundation /// Centralised local-notification sender. /// /// Uses notification identifiers as categories — posting the same id replaces /// any previous notification in that category, preventing stale banners from /// stacking up. enum NotificationService { static func notifyBackupStarted(count: Int) { guard count > 0 else { return } post( id: "kisani.backup.started", title: "Backup started", body: "Kisani is backing up \(count) new \(count == 1 ? "item" : "items")." ) } static func notifyBackupCompleted(uploaded: Int, total: Int) { post( id: "kisani.backup.completed", title: "Backup complete", body: total > 0 ? "All \(total) photos are safe." : "\(uploaded) \(uploaded == 1 ? "item" : "items") backed up." ) } static func notifyBackupPaused(pendingCount: Int) { post( id: "kisani.backup.paused", title: "Backup paused", body: pendingCount > 0 ? "Kisani will continue \(pendingCount) \(pendingCount == 1 ? "item" : "items") when your NAS is reachable." : "Kisani will continue when your NAS is reachable." ) } static func notifyBackupFailed(count: Int) { post( id: "kisani.backup.failed", title: "Backup failed", body: "\(count) \(count == 1 ? "item" : "items") could not be backed up. Tap to review." ) } static func notifyNASOffline() { post( id: "kisani.nas.offline", title: "NAS unreachable", body: "Kisani will resume backup when your NAS is back online." ) } // MARK: — Private private static func post(id: String, title: String, body: String) { let content = UNMutableNotificationContent() content.title = title content.body = body content.sound = .default let request = UNNotificationRequest(identifier: id, content: content, trigger: nil) UNUserNotificationCenter.current().add(request) { _ in } } }