BackgroundAlertManager.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. //
  2. // BackgroundAlertManager.swift
  3. // LoopFollow
  4. //
  5. // Created by Jonas Björkert on 2024-06-22.
  6. // Copyright © 2024 Jon Fawcett. All rights reserved.
  7. //
  8. import Foundation
  9. import UserNotifications
  10. /// Enum representing different background alert durations.
  11. enum BackgroundAlertDuration: TimeInterval, CaseIterable {
  12. case sixMinutes = 360 // 6 minutes in seconds
  13. case twelveMinutes = 720 // 12 minutes in seconds
  14. case eighteenMinutes = 1080 // 18 minutes in seconds
  15. }
  16. /// Enum representing unique identifiers for each background alert.
  17. enum BackgroundAlertIdentifier: String, CaseIterable {
  18. case sixMin = "loopfollow.background.alert.6min"
  19. case twelveMin = "loopfollow.background.alert.12min"
  20. case eighteenMin = "loopfollow.background.alert.18min"
  21. }
  22. class BackgroundAlertManager {
  23. static let shared = BackgroundAlertManager()
  24. private init() {}
  25. /// Flag indicating whether background alerts are currently scheduled.
  26. private var isAlertScheduled: Bool = false
  27. /// Title prefix for all background refresh notifications.
  28. private let notificationTitlePrefix = "LoopFollow Background Refresh"
  29. /// Start scheduling background alerts.
  30. func startBackgroundAlert() {
  31. isAlertScheduled = true
  32. scheduleBackgroundAlert()
  33. }
  34. /// Stop all scheduled background alerts.
  35. func stopBackgroundAlert() {
  36. isAlertScheduled = false
  37. cancelBackgroundAlerts()
  38. }
  39. /// Schedule all background alerts based on predefined durations.
  40. func scheduleBackgroundAlert() {
  41. guard isAlertScheduled, Storage.shared.backgroundRefreshType.value != .none else { return }
  42. let isBluetoothActive = Storage.shared.backgroundRefreshType.value.isBluetooth
  43. // Define all alerts in an array for scalability.
  44. let alerts: [BackgroundAlert] = [
  45. BackgroundAlert(
  46. identifier: BackgroundAlertIdentifier.sixMin.rawValue,
  47. timeInterval: BackgroundAlertDuration.sixMinutes.rawValue,
  48. body: isBluetoothActive
  49. ? "App inactive for 6 minutes. Verify Bluetooth connectivity."
  50. : "App inactive for 6 minutes. Open to resume."
  51. ),
  52. BackgroundAlert(
  53. identifier: BackgroundAlertIdentifier.twelveMin.rawValue,
  54. timeInterval: BackgroundAlertDuration.twelveMinutes.rawValue,
  55. body: isBluetoothActive
  56. ? "App inactive for 12 minutes. Verify Bluetooth connectivity."
  57. : "App inactive for 12 minutes. Open to resume."
  58. ),
  59. BackgroundAlert(
  60. identifier: BackgroundAlertIdentifier.eighteenMin.rawValue,
  61. timeInterval: BackgroundAlertDuration.eighteenMinutes.rawValue,
  62. body: isBluetoothActive
  63. ? "App inactive for 18 minutes. Verify Bluetooth connectivity."
  64. : "App inactive for 18 minutes. Open to resume."
  65. )
  66. ]
  67. // Schedule each alert.
  68. for alert in alerts {
  69. let content = createNotificationContent(for: notificationTitlePrefix, body: alert.body)
  70. let trigger = UNTimeIntervalNotificationTrigger(timeInterval: alert.timeInterval, repeats: false)
  71. let request = UNNotificationRequest(identifier: alert.identifier, content: content, trigger: trigger)
  72. UNUserNotificationCenter.current().add(request) { error in
  73. if let error = error {
  74. print("Error scheduling \(alert.timeInterval / 60)-minute background alert: \(error)")
  75. }
  76. }
  77. }
  78. }
  79. /// Create notification content with a given title and body.
  80. /// - Parameters:
  81. /// - title: The title of the notification.
  82. /// - body: The body text of the notification.
  83. /// - Returns: Configured `UNMutableNotificationContent` object.
  84. private func createNotificationContent(for title: String, body: String) -> UNMutableNotificationContent {
  85. let content = UNMutableNotificationContent()
  86. content.title = title
  87. content.body = body
  88. content.sound = .defaultCritical
  89. content.categoryIdentifier = "loopfollow.background.alert"
  90. return content
  91. }
  92. /// Cancel all scheduled background alerts.
  93. private func cancelBackgroundAlerts() {
  94. let identifiers = BackgroundAlertIdentifier.allCases.map { $0.rawValue }
  95. UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
  96. }
  97. }
  98. /// Struct representing a single background alert.
  99. struct BackgroundAlert {
  100. let identifier: String
  101. let timeInterval: TimeInterval
  102. let body: String
  103. }