docs: add comprehensive static daily reminders documentation

- Add static daily reminders to README.md core features and API reference
- Create detailed usage guide in USAGE.md with examples and best practices
- Add version 2.1.0 changelog entry documenting new reminder functionality
- Create examples/static-daily-reminders.ts with complete usage examples
- Update test-apps README to include reminder testing capabilities

The static daily reminder feature provides simple daily notifications
without network content dependency, supporting cross-platform scheduling
with rich customization options.
This commit is contained in:
Matthew Raymer
2025-10-05 05:12:06 +00:00
parent 9ec30974da
commit f9c21d4e5b
21 changed files with 2120 additions and 0 deletions

View File

@@ -176,4 +176,305 @@ public class DailyNotificationPlugin: CAPPlugin {
// For now, return next day at 9 AM
return 86400 // 24 hours
}
// MARK: - Static Daily Reminder Methods
@objc func scheduleDailyReminder(_ call: CAPPluginCall) {
guard let id = call.getString("id"),
let title = call.getString("title"),
let body = call.getString("body"),
let time = call.getString("time") else {
call.reject("Missing required parameters: id, title, body, time")
return
}
let sound = call.getBool("sound", true)
let vibration = call.getBool("vibration", true)
let priority = call.getString("priority", "normal")
let repeatDaily = call.getBool("repeatDaily", true)
let timezone = call.getString("timezone")
print("DNP-REMINDER: Scheduling daily reminder: \(id)")
// Parse time (HH:mm format)
let timeComponents = time.components(separatedBy: ":")
guard timeComponents.count == 2,
let hour = Int(timeComponents[0]),
let minute = Int(timeComponents[1]),
hour >= 0 && hour <= 23,
minute >= 0 && minute <= 59 else {
call.reject("Invalid time format. Use HH:mm (e.g., 09:00)")
return
}
// Create notification content
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = sound ? .default : nil
content.categoryIdentifier = "DAILY_REMINDER"
// Set priority
switch priority {
case "high":
content.interruptionLevel = .critical
case "low":
content.interruptionLevel = .passive
default:
content.interruptionLevel = .active
}
// Create date components for daily trigger
var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute
let trigger = UNCalendarNotificationTrigger(
dateMatching: dateComponents,
repeats: repeatDaily
)
let request = UNNotificationRequest(
identifier: "reminder_\(id)",
content: content,
trigger: trigger
)
// Store reminder in UserDefaults
storeReminderInUserDefaults(
id: id,
title: title,
body: body,
time: time,
sound: sound,
vibration: vibration,
priority: priority,
repeatDaily: repeatDaily,
timezone: timezone
)
// Schedule the notification
notificationCenter.add(request) { error in
DispatchQueue.main.async {
if let error = error {
print("DNP-REMINDER: Failed to schedule reminder: \(error)")
call.reject("Failed to schedule daily reminder: \(error.localizedDescription)")
} else {
print("DNP-REMINDER: Daily reminder scheduled successfully: \(id)")
call.resolve()
}
}
}
}
@objc func cancelDailyReminder(_ call: CAPPluginCall) {
guard let reminderId = call.getString("reminderId") else {
call.reject("Missing reminderId parameter")
return
}
print("DNP-REMINDER: Cancelling daily reminder: \(reminderId)")
// Cancel the notification
notificationCenter.removePendingNotificationRequests(withIdentifiers: ["reminder_\(reminderId)"])
// Remove from UserDefaults
removeReminderFromUserDefaults(id: reminderId)
call.resolve()
}
@objc func getScheduledReminders(_ call: CAPPluginCall) {
print("DNP-REMINDER: Getting scheduled reminders")
// Get pending notifications
notificationCenter.getPendingNotificationRequests { requests in
let reminderRequests = requests.filter { $0.identifier.hasPrefix("reminder_") }
// Get stored reminder data from UserDefaults
let reminders = self.getRemindersFromUserDefaults()
var result: [[String: Any]] = []
for reminder in reminders {
let isScheduled = reminderRequests.contains { $0.identifier == "reminder_\(reminder["id"] as! String)" }
var reminderInfo = reminder
reminderInfo["isScheduled"] = isScheduled
result.append(reminderInfo)
}
DispatchQueue.main.async {
call.resolve(["reminders": result])
}
}
}
@objc func updateDailyReminder(_ call: CAPPluginCall) {
guard let reminderId = call.getString("reminderId") else {
call.reject("Missing reminderId parameter")
return
}
print("DNP-REMINDER: Updating daily reminder: \(reminderId)")
// Cancel existing reminder
notificationCenter.removePendingNotificationRequests(withIdentifiers: ["reminder_\(reminderId)"])
// Update in UserDefaults
let title = call.getString("title")
let body = call.getString("body")
let time = call.getString("time")
let sound = call.getBool("sound")
let vibration = call.getBool("vibration")
let priority = call.getString("priority")
let repeatDaily = call.getBool("repeatDaily")
let timezone = call.getString("timezone")
updateReminderInUserDefaults(
id: reminderId,
title: title,
body: body,
time: time,
sound: sound,
vibration: vibration,
priority: priority,
repeatDaily: repeatDaily,
timezone: timezone
)
// Reschedule with new settings if all required fields are provided
if let title = title, let body = body, let time = time {
// Parse time
let timeComponents = time.components(separatedBy: ":")
guard timeComponents.count == 2,
let hour = Int(timeComponents[0]),
let minute = Int(timeComponents[1]) else {
call.reject("Invalid time format")
return
}
// Create new notification content
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = (sound ?? true) ? .default : nil
content.categoryIdentifier = "DAILY_REMINDER"
// Set priority
let finalPriority = priority ?? "normal"
switch finalPriority {
case "high":
content.interruptionLevel = .critical
case "low":
content.interruptionLevel = .passive
default:
content.interruptionLevel = .active
}
// Create date components for daily trigger
var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute
let trigger = UNCalendarNotificationTrigger(
dateMatching: dateComponents,
repeats: repeatDaily ?? true
)
let request = UNNotificationRequest(
identifier: "reminder_\(reminderId)",
content: content,
trigger: trigger
)
// Schedule the updated notification
notificationCenter.add(request) { error in
DispatchQueue.main.async {
if let error = error {
call.reject("Failed to reschedule updated reminder: \(error.localizedDescription)")
} else {
call.resolve()
}
}
}
} else {
call.resolve()
}
}
// MARK: - Helper Methods for Reminder Storage
private func storeReminderInUserDefaults(
id: String,
title: String,
body: String,
time: String,
sound: Bool,
vibration: Bool,
priority: String,
repeatDaily: Bool,
timezone: String?
) {
let reminderData: [String: Any] = [
"id": id,
"title": title,
"body": body,
"time": time,
"sound": sound,
"vibration": vibration,
"priority": priority,
"repeatDaily": repeatDaily,
"timezone": timezone ?? "",
"createdAt": Date().timeIntervalSince1970,
"lastTriggered": 0
]
var reminders = UserDefaults.standard.array(forKey: "daily_reminders") as? [[String: Any]] ?? []
reminders.append(reminderData)
UserDefaults.standard.set(reminders, forKey: "daily_reminders")
print("DNP-REMINDER: Reminder stored: \(id)")
}
private func removeReminderFromUserDefaults(id: String) {
var reminders = UserDefaults.standard.array(forKey: "daily_reminders") as? [[String: Any]] ?? []
reminders.removeAll { ($0["id"] as? String) == id }
UserDefaults.standard.set(reminders, forKey: "daily_reminders")
print("DNP-REMINDER: Reminder removed: \(id)")
}
private func getRemindersFromUserDefaults() -> [[String: Any]] {
return UserDefaults.standard.array(forKey: "daily_reminders") as? [[String: Any]] ?? []
}
private func updateReminderInUserDefaults(
id: String,
title: String?,
body: String?,
time: String?,
sound: Bool?,
vibration: Bool?,
priority: String?,
repeatDaily: Bool?,
timezone: String?
) {
var reminders = UserDefaults.standard.array(forKey: "daily_reminders") as? [[String: Any]] ?? []
for i in 0..<reminders.count {
if reminders[i]["id"] as? String == id {
if let title = title { reminders[i]["title"] = title }
if let body = body { reminders[i]["body"] = body }
if let time = time { reminders[i]["time"] = time }
if let sound = sound { reminders[i]["sound"] = sound }
if let vibration = vibration { reminders[i]["vibration"] = vibration }
if let priority = priority { reminders[i]["priority"] = priority }
if let repeatDaily = repeatDaily { reminders[i]["repeatDaily"] = repeatDaily }
if let timezone = timezone { reminders[i]["timezone"] = timezone }
break
}
}
UserDefaults.standard.set(reminders, forKey: "daily_reminders")
print("DNP-REMINDER: Reminder updated: \(id)")
}
}