import UserNotifications import Foundation class NotificationService { static let shared = NotificationService() // MARK: - Permission func requestPermission() async -> Bool { do { let granted = try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) return granted } catch { return false } } func isAuthorized() async -> Bool { let settings = await UNUserNotificationCenter.current().notificationSettings() return settings.authorizationStatus == .authorized } // MARK: - Morning Reminder func scheduleMorningReminder(hour: Int, minute: Int) { let content = UNMutableNotificationContent() content.title = "Доброе утро!" content.body = "Посмотри свои привычки и задачи на сегодня" content.sound = .default var components = DateComponents() components.hour = hour components.minute = minute let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true) let request = UNNotificationRequest(identifier: "morning_reminder", content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) } // MARK: - Evening Reminder func scheduleEveningReminder(hour: Int, minute: Int) { let content = UNMutableNotificationContent() content.title = "Итоги дня" content.body = "Проверь, все ли привычки выполнены сегодня" content.sound = .default var components = DateComponents() components.hour = hour components.minute = minute let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true) let request = UNNotificationRequest(identifier: "evening_reminder", content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) } // MARK: - Task Deadline Reminder func scheduleTaskReminder(taskId: Int, title: String, dueDate: Date) { let content = UNMutableNotificationContent() content.title = "Задача скоро" content.body = title content.sound = .default // За 1 час до дедлайна let reminderDate = dueDate.addingTimeInterval(-3600) guard reminderDate > Date() else { return } let components = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: reminderDate) let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false) let request = UNNotificationRequest(identifier: "task_\(taskId)", content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) } // MARK: - Habit Reminder func scheduleHabitReminder(habitId: Int, name: String, hour: Int, minute: Int) { let content = UNMutableNotificationContent() content.title = "Привычка" content.body = name content.sound = .default var components = DateComponents() components.hour = hour components.minute = minute let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true) let request = UNNotificationRequest(identifier: "habit_\(habitId)", content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) } // MARK: - Cancel func cancelReminder(_ identifier: String) { UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [identifier]) } func cancelAllReminders() { UNUserNotificationCenter.current().removeAllPendingNotificationRequests() } // MARK: - Payment Reminders func schedulePaymentReminders(payments: [MonthlyPaymentDetail]) { cancelPaymentReminders() let cal = Calendar.current let now = Date() for payment in payments { let day = payment.day guard day >= 1, day <= 28 else { continue } // Build due date for this month var components = cal.dateComponents([.year, .month], from: now) components.day = day components.hour = 11 components.minute = 0 guard let dueDate = cal.date(from: components) else { continue } let offsets = [(-5, "через 5 дней"), (-1, "завтра"), (0, "сегодня")] for (offset, label) in offsets { guard let notifDate = cal.date(byAdding: .day, value: offset, to: dueDate), notifDate > now else { continue } let content = UNMutableNotificationContent() content.title = "Платёж \(label)" content.body = "\(payment.categoryName): \(Int(payment.amount)) ₽ — \(day) числа" content.sound = .default let trigger = UNCalendarNotificationTrigger( dateMatching: cal.dateComponents([.year, .month, .day, .hour, .minute], from: notifDate), repeats: false ) let id = "payment_\(payment.categoryId)_\(offset)" UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: id, content: content, trigger: trigger)) } } } func cancelPaymentReminders() { let center = UNUserNotificationCenter.current() center.getPendingNotificationRequests { requests in let ids = requests.filter { $0.identifier.hasPrefix("payment_") }.map(\.identifier) center.removePendingNotificationRequests(withIdentifiers: ids) } } // MARK: - Update from settings func updateSchedule(morning: Bool, morningTime: String, evening: Bool, eveningTime: String) { cancelReminder("morning_reminder") cancelReminder("evening_reminder") if morning { let parts = morningTime.split(separator: ":").compactMap { Int($0) } if parts.count == 2 { scheduleMorningReminder(hour: parts[0], minute: parts[1]) } } if evening { let parts = eveningTime.split(separator: ":").compactMap { Int($0) } if parts.count == 2 { scheduleEveningReminder(hour: parts[0], minute: parts[1]) } } } }