Cancel-then-schedule was skipped because the idempotence check still found the cancelled PendingIntent in Android's cache. Skip PendingIntent idempotence on the cancel-then-schedule path so the new schedule is always set. - NotifyReceiver.scheduleExactNotification: add skipPendingIntentIdempotence (used only from scheduleDailyNotification) - ScheduleHelper: pass skipPendingIntentIdempotence=true after cancelNotification(scheduleId) - Version 1.1.2: package.json, CHANGELOG, README, TS/Android refs - docs/CONSUMING_APP_OPTIONAL_ANDROID_ID_CLEANUP.md: optional app cleanup to use one stable id on both platforms
4.9 KiB
Optional: Use a Single Stable Schedule ID on iOS and Android
Audience: Consuming apps (e.g. TimeSafari / crowd-funder-for-time-pwa) that use @timesafari/daily-notification-plugin.
Purpose: Describe an optional app-side cleanup now that the plugin’s Android second-schedule bug is fixed (plugin v1.1.2+).
Use: Feed this doc into Cursor (or any editor) in the consuming app repo when implementing the cleanup.
Context
- Plugin fix (v1.1.2): After cancel-then-schedule on Android, the plugin no longer skips the new schedule due to PendingIntent cache. Rescheduling works reliably whether or not the app passes an explicit
idtoscheduleDailyNotification. - Previous workaround: Some apps avoided passing
idon Android and used the plugin default"daily_notification"so that the (now-fixed) second-schedule bug would not trigger. On iOS they passed a stable id (e.g."daily_timesafari_reminder") for getStatus/cancel and verification. - Optional cleanup: You can use the same stable schedule id on both iOS and Android. That simplifies code (one id everywhere), makes getStatus/cancel and verification consistent across platforms, and is safe with plugin v1.1.2+.
Prerequisites
- Depend on
@timesafari/daily-notification-plugin@1.1.2(or^1.1.2) so the Android fix is in effect. - No other code changes are required for the bug fix; this doc is only for the optional id cleanup.
What to Change in the Consuming App
1. Single stable reminder ID (both platforms)
Use one reminder id for schedule, cancel, and getStatus on both iOS and Android.
Example (current pattern):
// Before: different id per platform
private get reminderId(): string {
return Capacitor.getPlatform() === "ios"
? "daily_timesafari_reminder"
: "daily_notification";
}
After (optional cleanup):
// After: same stable id on both platforms (requires plugin >= 1.1.2)
private readonly reminderId = "daily_timesafari_reminder";
Or keep a getter if you prefer:
private get reminderId(): string {
return "daily_timesafari_reminder";
}
Use whatever stable string your app already uses on iOS (e.g. "daily_timesafari_reminder"); no need to change the value.
2. Pass id when scheduling on Android
Today you may only add scheduleOptions.id on iOS. Add it for Android too so the plugin stores and returns this id (getStatus, getScheduledReminders, cancel all use it).
Example (current pattern):
const scheduleOptions = {
time: options.time,
title: options.title,
body: options.body,
sound: true,
priority: (options.priority || "normal") as "low" | "default" | "high",
};
if (Capacitor.getPlatform() === "ios") {
scheduleOptions.id = this.reminderId;
}
await DailyNotification.scheduleDailyNotification(scheduleOptions);
After (optional cleanup):
const scheduleOptions = {
time: options.time,
title: options.title,
body: options.body,
sound: true,
priority: (options.priority || "normal") as "low" | "default" | "high",
id: this.reminderId, // same id on iOS and Android (plugin >= 1.1.2)
};
await DailyNotification.scheduleDailyNotification(scheduleOptions);
So: always pass id: this.reminderId (or your chosen constant) for both platforms.
3. Update comments
Remove or update comments that say Android must not receive an id to avoid the second-schedule bug, and that the plugin uses "daily_notification" on Android. Replace with a short note that a single stable id is used on both platforms and requires plugin v1.1.2+.
Example comment to add/update:
/**
* Stable schedule/reminder ID used for schedule, cancel, and getStatus.
* Same value on iOS and Android (plugin v1.1.2+ fixes Android reschedule with custom id).
*/
private readonly reminderId = "daily_timesafari_reminder";
Files to Touch (typical)
- Native notification service (e.g.
src/services/notifications/NativeNotificationService.ts):reminderId: use single value for both platforms.scheduleDailyNotification: always passidinscheduleOptions(include Android).- Adjust comments as above.
No changes are required to cancel or getStatus if they already use this.reminderId; they will now resolve the same schedule on Android as on iOS.
Verification
- Android: Schedule a daily notification, then change time and save again (reschedule). The second scheduled time should fire; no need to reinstall.
- getStatus: After scheduling on Android, getStatus should return the scheduled reminder with the same id you pass (e.g.
daily_timesafari_reminder). - Cancel: Cancelling by that id on Android should clear the scheduled notification.
References
- Plugin CHANGELOG:
[1.1.2] - 2026-02-13— Android second daily notification not firing after reschedule. - Issue context (if present in consuming app):
doc/android-daily-notification-second-schedule-issue.md.