feat(ios): implement getNotificationStatus and cancelAllNotifications methods

Implemented status and cancellation methods matching Android functionality:

getNotificationStatus():
- Gets pending notifications from UNUserNotificationCenter
- Retrieves schedules from UserDefaults
- Calculates next notification time from schedules
- Returns status matching Android API structure
- Includes isEnabled, isScheduled, lastNotificationTime, nextNotificationTime, pending count

cancelAllNotifications():
- Removes all pending daily notifications from UNUserNotificationCenter
- Cancels background fetch tasks by identifier
- Clears notification schedules from UserDefaults
- Idempotent (safe to call multiple times)
- Matches Android behavior (cancels alarms, WorkManager jobs, database)

Helper Methods:
- getSchedulesFromUserDefaults() - Retrieves stored schedules
- Improved storeScheduleInUserDefaults() - Prevents duplicates

Progress: 12/52 methods implemented (23% complete)
This commit is contained in:
Matthew Raymer
2025-11-11 01:54:52 -08:00
parent a7dd559c4a
commit d7754752ba

View File

@@ -313,6 +313,11 @@ public class DailyNotificationPlugin: CAPPlugin {
body: String,
nextRunTime: TimeInterval
) {
var schedules = UserDefaults.standard.array(forKey: "DailyNotificationSchedules") as? [[String: Any]] ?? []
// Remove existing schedule with same ID if present
schedules.removeAll { ($0["id"] as? String) == id }
let schedule: [String: Any] = [
"id": id,
"kind": "notify",
@@ -324,13 +329,123 @@ public class DailyNotificationPlugin: CAPPlugin {
"createdAt": Date().timeIntervalSince1970 * 1000
]
var schedules = UserDefaults.standard.array(forKey: "DailyNotificationSchedules") as? [[String: Any]] ?? []
schedules.append(schedule)
UserDefaults.standard.set(schedules, forKey: "DailyNotificationSchedules")
print("DNP-PLUGIN: Schedule stored: \(id)")
}
// MARK: - Status & Cancellation Methods
/**
* Get notification status
*
* Returns the current status of scheduled notifications, including:
* - Whether notifications are enabled/scheduled
* - Last notification time
* - Next notification time
* - Pending notification count
*
* Equivalent to Android's getNotificationStatus method.
*/
@objc func getNotificationStatus(_ call: CAPPluginCall) {
// Get pending notifications from UNUserNotificationCenter
notificationCenter.getPendingNotificationRequests { requests in
// Filter for daily notifications (those starting with "daily_")
let dailyNotifications = requests.filter { $0.identifier.hasPrefix("daily_") }
// Get schedules from UserDefaults
let schedules = self.getSchedulesFromUserDefaults()
let notifySchedules = schedules.filter {
($0["kind"] as? String) == "notify" &&
($0["enabled"] as? Bool) == true
}
// Calculate next notification time
var nextNotificationTime: TimeInterval = 0
if let nextRunTimes = notifySchedules.compactMap({ $0["nextRunTime"] as? TimeInterval }) as? [TimeInterval],
!nextRunTimes.isEmpty {
nextNotificationTime = nextRunTimes.min() ?? 0
}
// Get last notification time from UserDefaults (stored when notification is delivered)
// For now, we'll use 0 if not available (history tracking can be added later)
let lastNotificationTime = UserDefaults.standard.double(forKey: "DailyNotificationLastDeliveryTime")
// Build result matching Android API
let result: [String: Any] = [
"isEnabled": !notifySchedules.isEmpty,
"isScheduled": !notifySchedules.isEmpty,
"lastNotificationTime": lastNotificationTime > 0 ? lastNotificationTime : 0,
"nextNotificationTime": nextNotificationTime,
"scheduledCount": notifySchedules.count,
"pending": dailyNotifications.count,
"settings": [
"enabled": !notifySchedules.isEmpty,
"count": notifySchedules.count
] as [String: Any]
]
DispatchQueue.main.async {
call.resolve(result)
}
}
}
/**
* Cancel all notifications
*
* Cancels all scheduled daily notifications:
* 1. Removes all pending notifications from UNUserNotificationCenter
* 2. Cancels all background fetch tasks
* 3. Clears schedules from UserDefaults
*
* Equivalent to Android's cancelAllNotifications method.
* The method is idempotent - safe to call multiple times.
*/
@objc func cancelAllNotifications(_ call: CAPPluginCall) {
print("DNP-PLUGIN: Cancelling all notifications")
// 1. Get all pending notifications
notificationCenter.getPendingNotificationRequests { requests in
let dailyNotificationIds = requests
.filter { $0.identifier.hasPrefix("daily_") }
.map { $0.identifier }
// 2. Remove all daily notifications
if !dailyNotificationIds.isEmpty {
self.notificationCenter.removePendingNotificationRequests(withIdentifiers: dailyNotificationIds)
print("DNP-PLUGIN: Removed \(dailyNotificationIds.count) pending notification(s)")
}
// 3. Cancel all background tasks
// Cancel by identifier (BGTaskScheduler requires identifier to cancel)
// Note: cancel() doesn't throw, it's safe to call even if task doesn't exist
self.backgroundTaskScheduler.cancel(taskRequestWithIdentifier: self.fetchTaskIdentifier)
self.backgroundTaskScheduler.cancel(taskRequestWithIdentifier: self.notifyTaskIdentifier)
print("DNP-PLUGIN: Cancelled background tasks")
// 4. Clear schedules from UserDefaults
var schedules = UserDefaults.standard.array(forKey: "DailyNotificationSchedules") as? [[String: Any]] ?? []
let notifySchedules = schedules.filter { ($0["kind"] as? String) == "notify" }
schedules.removeAll { ($0["kind"] as? String) == "notify" }
UserDefaults.standard.set(schedules, forKey: "DailyNotificationSchedules")
print("DNP-PLUGIN: Removed \(notifySchedules.count) schedule(s) from storage")
DispatchQueue.main.async {
print("DNP-PLUGIN: All notifications cancelled successfully")
call.resolve()
}
}
}
/**
* Get schedules from UserDefaults
*/
private func getSchedulesFromUserDefaults() -> [[String: Any]] {
return UserDefaults.standard.array(forKey: "DailyNotificationSchedules") as? [[String: Any]] ?? []
}
// MARK: - Private Implementation Methods
private func setupBackgroundTasks() {