Create unified alarm documentation system with strict role separation: - Doc A: Platform capability reference (canonical OS facts) - Doc B: Plugin behavior exploration (executable test harness) - Doc C: Plugin requirements (guarantees, JS/TS contract, traceability) Changes: - Add canonical rule to Doc A preventing platform fact duplication - Convert Doc B to pure executable test spec with scenario tables - Complete Doc C with guarantees matrix, JS/TS API contract, recovery contract, unsupported behaviors, and traceability matrix - Remove implementation details from unified directive - Add compliance milestone tracking and iOS parity gates - Add deprecation banners to legacy platform docs All documents now enforce strict role separation with cross-references to prevent duplication and ensure single source of truth.
1048 lines
48 KiB
Markdown
1048 lines
48 KiB
Markdown
# Plugin Requirements & Implementation Rules
|
|
|
|
**Author**: Matthew Raymer
|
|
**Date**: November 2025
|
|
**Status**: Active Requirements - Implementation Guide
|
|
**Version**: 1.1.0
|
|
**Last Synced With Plugin Version**: v1.1.0
|
|
|
|
## Purpose
|
|
|
|
This document defines the **rules the plugin MUST follow** to behave predictably across Android and iOS platforms. It specifies:
|
|
|
|
**Language Standards**: This document uses RFC-2119 keywords:
|
|
- **MUST** — Required for correctness. Violation breaks plugin guarantees.
|
|
- **SHOULD** — Strong recommendation. Exceptions MUST be documented.
|
|
- **MUST NOT** — Prohibited behavior. Plugin must not attempt this.
|
|
- **MAY** — Optional. Plugin may or may not implement.
|
|
|
|
**Cross-Reference Format**: All references use format `[Doc X §Y.Z]` where:
|
|
- `Doc A` = [Platform Capability Reference](./01-platform-capability-reference.md)
|
|
- `Doc B` = [Plugin Behavior Exploration](./02-plugin-behavior-exploration.md)
|
|
- `Phase N` = Implementation phase directive
|
|
|
|
* Plugin behavior guarantees and limitations
|
|
* Persistence requirements
|
|
* Recovery strategies
|
|
* Missed alarm handling contract
|
|
* JS/TS API contract and caveats
|
|
* Platform-specific requirements
|
|
* Testing requirements
|
|
|
|
**Reference**: See [Platform Capability Reference](./01-platform-capability-reference.md) for OS-level facts.
|
|
|
|
---
|
|
|
|
## 1. Plugin Behavior Guarantees & Limitations
|
|
|
|
### 1.1 Cross-Platform Guarantees Matrix
|
|
|
|
| Behavior | Android | iOS | Guarantee Level | Platform Source |
|
|
| -------- | ------- | --- | --------------- | --------------- |
|
|
| **Survives swipe-kill** | ✅ Yes | ✅ Yes | **MUST** (OS-guaranteed) | [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [Doc A §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination) |
|
|
| **Survives reboot** | ⚠️ Only if rescheduled | ✅ Yes | Android: **SHOULD** (best-effort), iOS: **MUST** (OS-guaranteed) | [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot), [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) |
|
|
| **Runs code on trigger** | ✅ Yes (PendingIntent) | ❌ No (never) | Android: **MUST**, iOS: **MUST NOT** | [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) |
|
|
| **Missed alarm detection** | ✅ Required | ✅ Required | **MUST** (Plugin-required) | [Doc A §2.1.4](./01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart) |
|
|
| **Force stop recovery** | ✅ Required | N/A | **MUST** (Plugin-required) | [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) |
|
|
| **Exact timing** | ⚠️ With permission | ⚠️ ±180s tolerance | Android: **SHOULD** (with permission), iOS: **SHOULD** (OS limitation) | [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree), [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) |
|
|
| **Background execution** | ⚠️ WorkManager | ⚠️ BGTaskScheduler | **SHOULD** (best-effort, system-controlled) | [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) |
|
|
|
|
**Guarantee Level Definitions**:
|
|
- **MUST** = Plugin guarantees this behavior
|
|
- **SHOULD** = Plugin attempts this but cannot guarantee (best-effort)
|
|
- **MUST NOT** = Plugin cannot and will not attempt this
|
|
|
|
### 1.2 Limitations by Platform
|
|
|
|
**See [§8 - Explicit Unsupported Behaviors](./03-plugin-requirements.md#8-explicit-unsupported-behaviors) for complete list.**
|
|
|
|
**Summary**:
|
|
* **Android Force Stop**: Cannot auto-recover - See [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop)
|
|
* **iOS Code Execution**: Cannot run code on notification fire - See [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires)
|
|
* **iOS Timing**: ±180s tolerance - See [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy)
|
|
* **Background Execution**: System-controlled, not guaranteed - See [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching)
|
|
|
|
---
|
|
|
|
## 2. Persistence Requirements
|
|
|
|
### 2.1 Required Persistence Items
|
|
|
|
The plugin **MUST** persist the following for each scheduled alarm/notification:
|
|
|
|
| Field | Type | Required | Purpose | Storage Location |
|
|
| ----- | ---- | -------- | ------- | ---------------- |
|
|
| `alarm_id` | String | ✅ Yes | Unique identifier | Schedule.id, NotificationContentEntity.id |
|
|
| `trigger_time` | Long/TimeInterval | ✅ Yes | When to fire | Schedule.nextRunAt, NotificationContentEntity.scheduledTime |
|
|
| `repeat_rule` | String/Enum | ✅ Yes | NONE, DAILY, WEEKLY, CUSTOM | Schedule.cron, Schedule.clockTime |
|
|
| `channel_id` | String | ✅ Yes | Notification channel (Android) | NotificationContentEntity (implicit) |
|
|
| `priority` | String/Int | ✅ Yes | Notification priority | NotificationContentEntity.priority |
|
|
| `title` | String | ✅ Yes | Notification title | NotificationContentEntity.title |
|
|
| `body` | String | ✅ Yes | Notification body | NotificationContentEntity.body |
|
|
| `sound_enabled` | Boolean | ✅ Yes | Sound preference | NotificationContentEntity.soundEnabled |
|
|
| `vibration_enabled` | Boolean | ✅ Yes | Vibration preference | NotificationContentEntity.vibrationEnabled |
|
|
| `payload` | String/JSON | ⚠️ Optional | Additional content | ContentCache.payload |
|
|
| `created_at` | Long/TimeInterval | ✅ Yes | Creation timestamp | NotificationContentEntity.createdAt |
|
|
| `updated_at` | Long/TimeInterval | ✅ Yes | Last update timestamp | NotificationContentEntity.updatedAt |
|
|
| `enabled` | Boolean | ✅ Yes | Whether alarm is active | Schedule.enabled |
|
|
|
|
### 2.2 Storage Implementation
|
|
|
|
**Android**:
|
|
* **Primary**: Room database (`DailyNotificationDatabase`)
|
|
* **Location**: `android/src/main/java/com/timesafari/dailynotification/`
|
|
* **Entities**: `Schedule`, `NotificationContentEntity`, `ContentCache`
|
|
|
|
**iOS**:
|
|
* **Primary**: UNUserNotificationCenter (OS-managed)
|
|
* **Secondary**: Plugin storage (UserDefaults, CoreData, or files)
|
|
* **Location**: `ios/Plugin/`
|
|
* **Component**: `DailyNotificationStorage?`
|
|
|
|
### 2.3 Persistence Validation
|
|
|
|
The plugin **MUST**:
|
|
* Validate persistence on every alarm schedule
|
|
* Log persistence failures
|
|
* Handle persistence errors gracefully
|
|
* Provide recovery mechanism if persistence fails
|
|
|
|
**Acceptance Criteria**:
|
|
* **Test**: Schedule alarm, kill app immediately, verify alarm persists in database
|
|
* **Test**: Schedule alarm, fill storage, verify graceful error handling
|
|
* **Test**: Schedule alarm, corrupt database, verify recovery mechanism
|
|
* **Pass**: All persistence operations succeed or fail gracefully with logging
|
|
|
|
### 2.4 Data Integrity Rules
|
|
|
|
**Required Validation**:
|
|
* **MUST** validate all required fields before persistence
|
|
* **MUST** validate schedule format (cron or HH:mm)
|
|
* **MUST** validate trigger time is in the future (or handle past times appropriately)
|
|
* **MUST** ensure foreign key relationships are valid (Schedule → NotificationContentEntity)
|
|
|
|
**Data Integrity Constraints**:
|
|
* `alarm_id` **MUST** be unique across all schedules
|
|
* `scheduleId` **MUST** reference an existing Schedule.id
|
|
* `scheduledTime` **MUST** be a valid timestamp
|
|
* `deliveryStatus` **MUST** be one of: "pending", "delivered", "missed"
|
|
|
|
### 2.5 Failure Modes
|
|
|
|
**Persistence Failure Handling**:
|
|
* Database errors: Log error, continue with alarm scheduling (best effort), retry persistence
|
|
* Storage full: Log warning, attempt cleanup, retry, notify user if critical
|
|
* Invalid data: Skip invalid entries, log warning, continue processing
|
|
* Migration failures: Rollback to backup, notify user, preserve existing data
|
|
|
|
---
|
|
|
|
## 3. Recovery Contract
|
|
|
|
### 3.1 Recovery Triggers & Required Actions
|
|
|
|
The plugin **MUST** implement recovery at the following points with the specified actions. This is the complete recovery contract that defines what the plugin guarantees for each recovery scenario.
|
|
|
|
#### 3.1.1 Boot Event (Android Only)
|
|
|
|
**Trigger**: `BOOT_COMPLETED` broadcast
|
|
|
|
**Required Actions**:
|
|
1. Load all enabled alarms from persistent storage
|
|
2. Reschedule each alarm using AlarmManager
|
|
3. Detect missed alarms (trigger_time < now)
|
|
4. Generate missed alarm events/notifications
|
|
5. Log recovery actions
|
|
|
|
**Code Reference**: `BootReceiver.kt` line 24
|
|
|
|
**Implementation Status**: See [Phase 3 Implementation](../android-implementation-directive-phase3.md)
|
|
|
|
**Platform Reference**: [Android §2.1.2](../01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot)
|
|
|
|
**Acceptance Criteria**:
|
|
* **Test**: Schedule alarm for 10 minutes, reboot device, do NOT open app, wait 15 minutes
|
|
* **Expected**: Alarm does NOT fire (OS limitation - see [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot))
|
|
* **Test**: Open app after reboot
|
|
* **Expected**: Plugin detects missed alarm, reschedules future alarms
|
|
* **Pass**: Missed alarm detected exactly once, future alarms rescheduled
|
|
|
|
**Missed Alarm Detection Acceptance Criteria**:
|
|
* **Test**: Create alarm scheduled for 2 minutes, kill app, wait 5 minutes, launch app
|
|
* **Expected**: Plugin detects missed alarm, marks in database (`delivery_status = 'missed'`)
|
|
* **Test**: Create repeating alarm, miss 3 occurrences, launch app
|
|
* **Expected**: Plugin detects all 3 missed alarms, reschedules next occurrence
|
|
* **Pass**: All missed alarms detected and marked, no duplicates, future alarms rescheduled
|
|
|
|
---
|
|
|
|
#### 3.1.2 App Cold Start
|
|
|
|
**Trigger**: App launched from terminated state
|
|
|
|
**Required Actions**:
|
|
1. Load all enabled alarms from persistent storage
|
|
2. Verify active alarms match stored alarms
|
|
3. Detect missed alarms (trigger_time < now)
|
|
4. Reschedule future alarms
|
|
5. Generate missed alarm events/notifications
|
|
6. Log recovery actions
|
|
|
|
**Implementation Status**: See [Phase 1 Implementation](../android-implementation-directive-phase1.md)
|
|
|
|
**Code Location**: Plugin initialization (`DailyNotificationPlugin.load()` or equivalent)
|
|
|
|
**Platform Reference**: [Android §2.1.4](../01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart)
|
|
|
|
**Acceptance Criteria**:
|
|
* **Test**: Schedule alarm for 5 minutes, kill app process (not force stop), wait 10 minutes
|
|
* **Expected**: Alarm fires (OS handles)
|
|
* **Test**: Launch app after alarm time passed
|
|
* **Expected**: Plugin detects missed alarm if alarm did not fire, reschedules future alarms
|
|
* **Pass**: Missed alarm detected if applicable, future alarms verified/rescheduled
|
|
|
|
---
|
|
|
|
#### 3.1.3 App Warm Start
|
|
|
|
**Trigger**: App returning from background
|
|
|
|
**Required Actions**:
|
|
1. Verify active alarms are still scheduled
|
|
2. Detect missed alarms (trigger_time < now)
|
|
3. Reschedule if needed
|
|
4. Log recovery actions
|
|
|
|
**Implementation Status**: Deferred to future phase
|
|
|
|
**Platform Reference**: [Android §2.1.4](../01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart)
|
|
|
|
---
|
|
|
|
#### 3.1.4 Force Stop Recovery (Android Only)
|
|
|
|
**Trigger**: App launched after Force Stop
|
|
|
|
**Required Actions**:
|
|
1. Detect force stop scenario (DB has alarms, AlarmManager has zero)
|
|
2. Mark all past alarms as missed
|
|
3. Reschedule all future alarms
|
|
4. Generate missed alarm events/notifications
|
|
5. Log recovery actions
|
|
|
|
**Implementation Status**: See [Phase 2 Implementation](../android-implementation-directive-phase2.md)
|
|
|
|
**Platform Reference**: [Android §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop), [Android §2.2.2](./01-platform-capability-reference.md#222-you-cannot-auto-resume-after-force-stop)
|
|
|
|
**Acceptance Criteria**:
|
|
* **Test**: Schedule multiple alarms (past and future), force stop app, wait past scheduled times
|
|
* **Expected**: No alarms fire (OS limitation)
|
|
* **Test**: Open app after force stop
|
|
* **Expected**: Plugin detects force stop scenario, marks all past alarms as missed, reschedules all future alarms
|
|
* **Pass**: All past alarms marked as missed, all future alarms rescheduled
|
|
|
|
---
|
|
|
|
#### 3.1.5 User Taps Notification
|
|
|
|
**Trigger**: User interaction with notification
|
|
|
|
**Required Actions**:
|
|
1. Launch app (OS handles)
|
|
2. Detect if notification was missed
|
|
3. Handle notification action
|
|
4. Update alarm state if needed
|
|
|
|
**Implementation Status**: Basic support exists
|
|
|
|
**Platform Reference**: [Android §2.1.1](../01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [iOS §3.1.1](../01-platform-capability-reference.md#311-notifications-survive-app-termination)
|
|
|
|
---
|
|
|
|
### 3.2 Recovery Matrix
|
|
|
|
| Recovery Point | Android | iOS | Implementation Phase |
|
|
| -------------- | ------- | --- | -------------------- |
|
|
| Boot event | ✅ Required | ❌ Not needed (OS handles) | Phase 3 |
|
|
| Cold start | ✅ Required | ✅ Required | Phase 1 |
|
|
| Warm start | ⚠️ Optional | ⚠️ Optional | Future |
|
|
| Force stop | ✅ Required | N/A | Phase 2 |
|
|
| User tap | ✅ Required | ✅ Required | Basic |
|
|
|
|
---
|
|
|
|
## 4. Missed Alarm Handling Contract
|
|
|
|
### 4.1 Definition
|
|
|
|
An alarm is "missed" if:
|
|
* `trigger_time < now`
|
|
* Alarm was not fired (or firing status unknown)
|
|
* Alarm is still enabled
|
|
* Alarm has not been manually cancelled
|
|
|
|
### 4.2 Detection Triggers
|
|
|
|
Missed alarms **MUST** be detected at:
|
|
1. **App cold start** - When app launches from terminated state
|
|
2. **App warm start** - When app returns from background (optional, future phase)
|
|
3. **Boot event** (Android) - When device reboots
|
|
4. **Force stop recovery** (Android) - When app launches after force stop
|
|
|
|
### 4.3 Required Actions
|
|
|
|
When a missed alarm is detected, the plugin **MUST**:
|
|
|
|
1. **Detect** missed alarms during recovery
|
|
2. **Mark** missed alarms in database (`delivery_status = 'missed'`)
|
|
3. **Generate** missed alarm event/notification (optional, future phase)
|
|
4. **Reschedule** future occurrences (if repeating)
|
|
5. **Log** missed alarm for debugging
|
|
6. **Update** alarm state (mark as missed or reschedule)
|
|
|
|
### 4.4 Missed Alarm Resolution Rules
|
|
|
|
**Resolution Strategy**:
|
|
|
|
| Scenario | Detection Time | Resolution Action | Next Occurrence |
|
|
| -------- | -------------- | ----------------- | --------------- |
|
|
| **Single alarm missed** | Cold start | Mark as missed, do not reschedule | N/A (one-time alarm) |
|
|
| **Repeating alarm missed** | Cold start | Mark as missed, reschedule next occurrence | Calculate from current time |
|
|
| **Multiple alarms missed** | Force stop recovery | Mark all as missed, reschedule all future | Calculate from current time |
|
|
| **Boot recovery** | Boot event | Mark past as missed, reschedule all future | Calculate from current time |
|
|
|
|
**State Transition Table**:
|
|
|
|
```
|
|
NONE (first launch, empty DB)
|
|
↓
|
|
COLD_START (DB populated, alarms may exist)
|
|
↓
|
|
FORCE_STOP? (detect: DB populated & no scheduled alarms)
|
|
↓
|
|
BOOT (detect: SharedPreferences flag set)
|
|
↓
|
|
Recovery Actions:
|
|
- Detect missed alarms
|
|
- Mark missed in database
|
|
- Reschedule future alarms
|
|
- Update state
|
|
```
|
|
|
|
**Transition Detection**:
|
|
* **NONE → COLD_START**: DB empty on first launch
|
|
* **COLD_START → FORCE_STOP**: DB has schedules but AlarmManager has zero alarms
|
|
* **COLD_START → BOOT**: SharedPreferences `last_boot_at` flag set
|
|
* **Any → Recovery**: Execute recovery actions based on scenario
|
|
|
|
### 4.5 Implementation Requirements
|
|
|
|
* **MUST** run on app launch (cold/warm start)
|
|
* **MUST** run on boot (Android)
|
|
* **MUST NOT** duplicate missed alarm notifications
|
|
* **MUST** handle timezone changes
|
|
* **MUST** handle clock adjustments
|
|
|
|
**Implementation Status**:
|
|
- Phase 1: Basic detection and marking - See [Phase 1 Implementation](../android-implementation-directive-phase1.md)
|
|
- Phase 2: Force stop recovery - See [Phase 2 Implementation](../android-implementation-directive-phase2.md)
|
|
- Phase 3: Boot recovery - See [Phase 3 Implementation](../android-implementation-directive-phase3.md)
|
|
- Future: Event emission, user notifications
|
|
|
|
---
|
|
|
|
## 5. JS/TS API Contract
|
|
|
|
### 5.1 JS/TS API Contract
|
|
|
|
The plugin **MUST** document and guarantee the following behaviors to JavaScript/TypeScript developers:
|
|
|
|
#### 5.1.1 `scheduleDailyNotification(options)`
|
|
|
|
**Method Signature**:
|
|
```typescript
|
|
scheduleDailyNotification(options: {
|
|
schedule: string; // Cron or HH:mm format
|
|
title: string;
|
|
body: string;
|
|
sound?: boolean;
|
|
vibration?: boolean;
|
|
priority?: 'high' | 'normal' | 'low';
|
|
payload?: object;
|
|
}): Promise<{
|
|
success: boolean;
|
|
alarmId?: string;
|
|
error?: {
|
|
code: string;
|
|
message: string;
|
|
action?: 'opened_settings' | 'permission_denied';
|
|
};
|
|
}>
|
|
```
|
|
|
|
**Returned Fields**:
|
|
* `success` (boolean) - Whether scheduling succeeded
|
|
* `alarmId` (string, optional) - Unique identifier for scheduled alarm
|
|
* `error` (object, optional) - Error details if scheduling failed
|
|
* `code` (string) - Error code for programmatic handling
|
|
* `message` (string) - Human-readable error message
|
|
* `action` (string, optional) - Action taken (e.g., "opened_settings")
|
|
|
|
**Guarantees**:
|
|
* ✅ Notification will fire if app is swiped from recents - **MUST** (OS-guaranteed)
|
|
* ✅ Notification will fire if app is terminated by OS - **MUST** (OS-guaranteed)
|
|
* ⚠️ Notification will fire after reboot **only if**:
|
|
* Android: Boot receiver is registered and working - **SHOULD** (best-effort)
|
|
* iOS: Automatic (OS handles) - **MUST** (OS-guaranteed)
|
|
* ❌ Notification will **NOT** fire after Android Force Stop until app is opened - **MUST NOT** (OS limitation)
|
|
* ⚠️ iOS notifications have ±180s timing tolerance - **SHOULD** (OS limitation)
|
|
|
|
**Platform Caveats**:
|
|
* Android requires `SCHEDULE_EXACT_ALARM` permission on Android 12+ - See [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree)
|
|
* Android requires `RECEIVE_BOOT_COMPLETED` permission for reboot recovery - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot)
|
|
* iOS requires notification authorization - See [Doc A §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination)
|
|
|
|
**Error States**:
|
|
* `EXACT_ALARM_PERMISSION_REQUIRED` - Android 12+ exact alarm permission needed
|
|
* **Response**: Plugin opens system settings, returns `action: "opened_settings"`
|
|
* `NOTIFICATIONS_DENIED` - Notification permission denied
|
|
* **Response**: Plugin shows error message, returns `action: "permission_denied"`
|
|
* `SCHEDULE_FAILED` - Scheduling failed (check logs)
|
|
* **Response**: Plugin logs error, returns error with message
|
|
* `STORAGE_FULL` - Cannot persist alarm (storage full)
|
|
* **Response**: Plugin attempts cleanup, retries, or returns error
|
|
* `INVALID_SCHEDULE` - Schedule format invalid
|
|
* **Response**: Plugin returns error with validation message
|
|
|
|
**Platform References**:
|
|
* [Android §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents)
|
|
* [Android §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot)
|
|
* [Android §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop)
|
|
* [iOS §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination)
|
|
* [iOS §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot)
|
|
|
|
**Developer Responsibilities**:
|
|
* **MUST** handle permission requests (Android 12+ exact alarm, iOS notification authorization)
|
|
* **MUST** handle error responses and provide user feedback
|
|
* **SHOULD** check `getNotificationStatus()` to verify alarm scheduling
|
|
* **MUST NOT** assume alarms will fire after Force Stop (Android) until app is opened
|
|
* **MUST NOT** assume exact timing on iOS (±180s tolerance)
|
|
* **SHOULD** implement retry logic for scheduling failures
|
|
|
|
**Platform Differences**:
|
|
* **Android**: Requires boot receiver for reboot recovery - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot)
|
|
* **iOS**: Notifications persist automatically across reboot - See [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot)
|
|
* **Android**: App code runs when alarm fires - See [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents)
|
|
* **iOS**: App code does NOT run when notification fires - See [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires)
|
|
|
|
---
|
|
|
|
#### 5.1.2 `scheduleDailyReminder(options)`
|
|
|
|
**Method Signature**: Same as `scheduleDailyNotification()` but without `payload` option.
|
|
|
|
**Guarantees**:
|
|
* Same as `scheduleDailyNotification()` above
|
|
* Static reminder (no content dependency)
|
|
* Fires even if content fetch fails
|
|
|
|
---
|
|
|
|
#### 5.1.3 `getNotificationStatus()`
|
|
|
|
**Method Signature**:
|
|
```typescript
|
|
getNotificationStatus(): Promise<{
|
|
pendingCount: number;
|
|
lastNotificationTime?: number;
|
|
missedAlarms?: Array<{
|
|
alarmId: string;
|
|
scheduledTime: number;
|
|
detectedAt: number;
|
|
}>;
|
|
permissions: {
|
|
exactAlarm?: boolean; // Android 12+
|
|
notifications?: boolean; // iOS
|
|
bootReceiver?: boolean; // Android
|
|
};
|
|
}>
|
|
```
|
|
|
|
**Returned Fields**:
|
|
* `pendingCount` (number) - Number of scheduled alarms
|
|
* `lastNotificationTime` (number, optional) - Timestamp of last notification
|
|
* `missedAlarms` (array, optional) - Array of missed alarms
|
|
* `alarmId` (string) - Unique identifier
|
|
* `scheduledTime` (number) - When alarm was scheduled to fire
|
|
* `detectedAt` (number) - When missed alarm was detected
|
|
* `permissions` (object) - Current permission status
|
|
* `exactAlarm` (boolean, optional) - Android 12+ exact alarm permission
|
|
* `notifications` (boolean, optional) - iOS notification authorization
|
|
* `bootReceiver` (boolean, optional) - Android boot receiver capability
|
|
|
|
**Guarantees**:
|
|
* Returns current notification status - **MUST**
|
|
* Includes pending notifications - **MUST**
|
|
* Includes last notification time - **SHOULD**
|
|
* May include missed alarm information - **SHOULD** (if detected)
|
|
|
|
---
|
|
|
|
### 5.2 API Warnings
|
|
|
|
The plugin **MUST** document the following warnings in JS/TS API documentation:
|
|
|
|
**Android Warnings**:
|
|
* "Notifications will not fire after device reboot unless the app is opened at least once" - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot)
|
|
* "Force Stop will prevent all notifications until the app is manually opened" - See [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop)
|
|
* "Exact alarm permission is required on Android 12+ for precise timing" - See [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree)
|
|
|
|
**iOS Warnings**:
|
|
* "Notifications have ±180 seconds timing tolerance" - See [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy)
|
|
* "App code does not run when notifications fire (unless user interacts)" - See [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires)
|
|
* "Background execution is system-controlled and not guaranteed" - See [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching)
|
|
|
|
**Cross-Platform Warnings**:
|
|
* "Missed alarms are detected on app launch, not at trigger time" - See [Doc C §4.2](./03-plugin-requirements.md#42-detection-triggers)
|
|
* "Repeating alarms must be rescheduled for each occurrence (Android)" - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot)
|
|
* "Location-based triggers do not persist across reboot (iOS)" - See [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot)
|
|
|
|
---
|
|
|
|
### 5.3 API Error Handling
|
|
|
|
The plugin **MUST**:
|
|
* Return clear error messages
|
|
* Include error codes for programmatic handling
|
|
* Open system settings when permission is needed
|
|
* Provide actionable guidance in error messages
|
|
|
|
**Example Error Response**:
|
|
```typescript
|
|
{
|
|
code: "EXACT_ALARM_PERMISSION_REQUIRED",
|
|
message: "Exact alarm permission required. Please grant 'Alarms & reminders' permission in Settings, then try again.",
|
|
action: "opened_settings" // or "permission_denied"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Plugin WILL NOT Guarantee
|
|
|
|
### 6.1 Hard Boundaries (Cannot Ever Guarantee)
|
|
|
|
The plugin **MUST NOT** promise or attempt to guarantee:
|
|
|
|
| Behavior | Platform | Reason | Platform Reference |
|
|
| -------- | -------- | ------ | ------------------ |
|
|
| **Auto-recovery after Force Stop** | Android | OS hard kill - cannot bypass | [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) |
|
|
| **App code execution on iOS notification fire** | iOS | OS limitation - code does not run | [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) |
|
|
| **Exact timing on iOS** | iOS | ±180s tolerance is OS limitation | [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) |
|
|
| **Background execution timing** | iOS | System-controlled, not guaranteed | [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) |
|
|
| **Alarm persistence without storage** | Both | Must persist in durable storage | [Doc A §2.2.3](./01-platform-capability-reference.md#223-alarms-cannot-be-preserved-solely-in-ram) |
|
|
|
|
**Enforcement**: These behaviors **MUST** be documented as unsupported in JS/TS API documentation.
|
|
|
|
---
|
|
|
|
### 6.2 Guarantee vs Best-Effort Matrix
|
|
|
|
| Behavior | Android | iOS | Guarantee Level | Notes |
|
|
| -------- | ------- | --- | -------------- | ----- |
|
|
| **Notification fires after swipe** | ✅ Guaranteed | ✅ Guaranteed | **MUST** - OS-guaranteed | [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [Doc A §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination) |
|
|
| **Notification fires after reboot** | ⚠️ Best-effort | ✅ Guaranteed | Android: **SHOULD** (requires boot receiver), iOS: **MUST** | [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot), [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) |
|
|
| **Missed alarm detection** | ✅ Guaranteed | ✅ Guaranteed | **MUST** - Plugin-required | [Doc C §4.2](./03-plugin-requirements.md#42-detection-triggers) |
|
|
| **Force stop recovery** | ✅ Guaranteed | N/A | **MUST** - Plugin-required | [Doc C §3.1.4](./03-plugin-requirements.md#314-force-stop-recovery-android-only) |
|
|
| **Exact timing** | ⚠️ Best-effort | ⚠️ Best-effort | Android: **SHOULD** (with permission), iOS: **SHOULD** (±180s) | [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree), [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) |
|
|
| **Background execution** | ⚠️ Best-effort | ⚠️ Best-effort | **SHOULD** - System-controlled | [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) |
|
|
|
|
**Legend**:
|
|
- **MUST** = Plugin guarantees this behavior
|
|
- **SHOULD** = Plugin attempts this but cannot guarantee (best-effort)
|
|
- **MUST NOT** = Plugin cannot and will not attempt this
|
|
|
|
---
|
|
|
|
### 6.3 Failure Modes & Required Responses
|
|
|
|
| Failure Mode | Platform | Required Plugin Response | User Impact |
|
|
| ------------ | -------- | ------------------------ | ----------- |
|
|
| **Persistence failure** | Both | Log error, continue with alarm scheduling (best effort), retry persistence | Alarm may be lost if app killed |
|
|
| **Storage full** | Both | Log warning, attempt cleanup, retry, notify user if critical | User must free space |
|
|
| **Invalid data** | Both | Skip invalid entries, log warning, continue processing | Invalid alarms ignored |
|
|
| **Permission denied** | Android | Open settings, show error message, return error code | User must grant permission |
|
|
| **Boot receiver not registered** | Android | Log error, alarms not rescheduled on boot | Alarms lost on reboot |
|
|
| **Force stop** | Android | Detect on app restart, recover all alarms, mark missed | Alarms lost until app opened |
|
|
| **Background execution denied** | iOS | Log warning, schedule next attempt, notify user | Prefetch may fail |
|
|
| **Notification authorization denied** | iOS | Show error, return error code, cannot schedule | Notifications disabled |
|
|
|
|
**Required Actions**:
|
|
* **MUST** log all failures with sufficient context
|
|
* **MUST** provide user-visible error messages for critical failures
|
|
* **SHOULD** provide recovery mechanisms where possible
|
|
* **MUST NOT** crash or silently fail
|
|
|
|
---
|
|
|
|
## 7. Explicit Storage Schema
|
|
|
|
### 7.1 Required Database Schema
|
|
|
|
**Android (Room Database)**:
|
|
|
|
| Table | Field | Type | Required | Purpose |
|
|
| ----- | ----- | ----- | -------- | ------- |
|
|
| **Schedule** | `id` | String | ✅ Yes | Unique identifier |
|
|
| | `kind` | String | ✅ Yes | "notify" or "fetch" |
|
|
| | `enabled` | Boolean | ✅ Yes | Whether alarm is active |
|
|
| | `cron` | String? | ⚠️ Optional | Cron expression |
|
|
| | `clockTime` | String? | ⚠️ Optional | HH:mm format |
|
|
| | `nextRunAt` | Long? | ⚠️ Optional | Next scheduled time |
|
|
| | `createdAt` | Long | ✅ Yes | Creation timestamp |
|
|
| | `updatedAt` | Long | ✅ Yes | Last update timestamp |
|
|
| **NotificationContentEntity** | `id` | String | ✅ Yes | Unique identifier |
|
|
| | `scheduleId` | String | ✅ Yes | Foreign key to Schedule |
|
|
| | `title` | String | ✅ Yes | Notification title |
|
|
| | `body` | String | ✅ Yes | Notification body |
|
|
| | `scheduledTime` | Long | ✅ Yes | When to fire |
|
|
| | `deliveryStatus` | String | ✅ Yes | "pending", "delivered", "missed" |
|
|
| | `priority` | String | ✅ Yes | Notification priority |
|
|
| | `soundEnabled` | Boolean | ✅ Yes | Sound preference |
|
|
| | `vibrationEnabled` | Boolean | ✅ Yes | Vibration preference |
|
|
| | `createdAt` | Long | ✅ Yes | Creation timestamp |
|
|
| | `updatedAt` | Long | ✅ Yes | Last update timestamp |
|
|
| **ContentCache** | `id` | String | ✅ Yes | Unique identifier |
|
|
| | `scheduleId` | String | ✅ Yes | Foreign key to Schedule |
|
|
| | `payload` | String? | ⚠️ Optional | Additional content (JSON) |
|
|
| | `createdAt` | Long | ✅ Yes | Creation timestamp |
|
|
|
|
**iOS (Plugin Storage)**:
|
|
|
|
| Storage Type | Field | Type | Required | Purpose |
|
|
| ------------ | ----- | ----- | -------- | ------- |
|
|
| **UserDefaults/CoreData** | `alarm_id` | String | ✅ Yes | Unique identifier |
|
|
| | `trigger_time` | TimeInterval | ✅ Yes | When to fire |
|
|
| | `repeat_rule` | String | ✅ Yes | NONE, DAILY, WEEKLY, CUSTOM |
|
|
| | `title` | String | ✅ Yes | Notification title |
|
|
| | `body` | String | ✅ Yes | Notification body |
|
|
| | `enabled` | Boolean | ✅ Yes | Whether alarm is active |
|
|
| | `created_at` | TimeInterval | ✅ Yes | Creation timestamp |
|
|
| | `updated_at` | TimeInterval | ✅ Yes | Last update timestamp |
|
|
|
|
**Note**: iOS also uses UNUserNotificationCenter (OS-managed) for notification scheduling, but plugin **MUST** maintain parallel storage for missed detection.
|
|
|
|
---
|
|
|
|
## 8. Explicit Unsupported Behaviors
|
|
|
|
### 8.1 Hard Boundaries (Plugin WILL NOT Guarantee)
|
|
|
|
The plugin **MUST NOT** support or guarantee the following behaviors:
|
|
|
|
| Behavior | Platform | Reason | Platform Reference |
|
|
| -------- | -------- | ------ | ------------------ |
|
|
| **Auto-recovery after Android Force Stop** | Android | OS hard kill - cannot bypass | [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) |
|
|
| **App code execution on iOS notification fire** | iOS | OS limitation - code does not run | [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) |
|
|
| **Exact timing on iOS** | iOS | ±180s tolerance is OS limitation | [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) |
|
|
| **Background execution timing guarantees** | Both | System-controlled, not guaranteed | [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) |
|
|
| **Alarm persistence without durable storage** | Both | Must use database/files | [Doc A §2.2.3](./01-platform-capability-reference.md#223-alarms-cannot-be-preserved-solely-in-ram) |
|
|
| **Repeating alarms without rescheduling** | Android | Must reschedule each occurrence | [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) |
|
|
| **Location-based triggers** | iOS | Does not persist across reboot | [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) |
|
|
| **Silent rescheduling without user launch** | Android | Force stop blocks all execution | [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) |
|
|
|
|
**Documentation Requirement**: All unsupported behaviors **MUST** be documented in JS/TS API with clear warnings. See [§5.2 - API Warnings](./03-plugin-requirements.md#52-api-warnings).
|
|
|
|
---
|
|
|
|
### 8.2 Platform-Specific Limitations
|
|
|
|
**Android**:
|
|
* Force Stop is a hard kill that cannot be bypassed - See [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop)
|
|
* Reboot wipes all alarms; must reschedule from storage - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot)
|
|
* Exact alarm permission required on Android 12+ - See [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree)
|
|
* Doze mode may defer inexact alarms - See [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents)
|
|
|
|
**iOS**:
|
|
* Background execution is severely limited - See [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching)
|
|
* App code does not run when notifications fire - See [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires)
|
|
* Timing has ±180s tolerance - See [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy)
|
|
* BGTaskScheduler execution is not guaranteed - See [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching)
|
|
|
|
---
|
|
|
|
## 9. Traceability Matrix
|
|
|
|
**Complete mapping from platform facts → requirements → tests → implementation**:
|
|
|
|
| Requirement | Platform Source (Doc A) | Expected Plugin Behavior (Doc C) | Test Scenario (Doc B) | Implemented in Phase |
|
|
| ----------- | ----------------------- | ------------------------------- | --------------------- | -------------------- |
|
|
| **Missed alarm detection** | [Android §2.1.4](./01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart) | [§4.2 - Detection Triggers](./03-plugin-requirements.md#42-detection-triggers) | [Test 4 Step 6](./02-plugin-behavior-exploration.md#test-4-device-reboot) | Phase 1 |
|
|
| **Boot reschedule** | [Android §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) | [§3.1.1 - Boot Event](./03-plugin-requirements.md#311-boot-event-android-only) | [Test 4 Step 7](./02-plugin-behavior-exploration.md#test-4-device-reboot) | Phase 3 |
|
|
| **Force stop recovery** | [Android §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) | [§3.1.4 - Force Stop Recovery](./03-plugin-requirements.md#314-force-stop-recovery-android-only) | [Test 5](./02-plugin-behavior-exploration.md#test-5-android-force-stop) | Phase 2 |
|
|
| **Cold start recovery** | [Android §2.1.4](./01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart) | [§3.1.2 - App Cold Start](./03-plugin-requirements.md#312-app-cold-start) | [Test 4 Step 5](./02-plugin-behavior-exploration.md#test-4-device-reboot) | Phase 1 |
|
|
| **Swipe from recents** | [Android §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [iOS §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination) | [§1.1 - Guarantees Matrix](./03-plugin-requirements.md#11-cross-platform-guarantees-matrix) | [Test 2](./02-plugin-behavior-exploration.md#test-2-swipe-from-recents) | OS-guaranteed |
|
|
| **iOS notification persistence** | [iOS §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) | [§1.1 - Guarantees Matrix](./03-plugin-requirements.md#11-cross-platform-guarantees-matrix) | [Test 3](./02-plugin-behavior-exploration.md#test-3-device-reboot-1) | OS-guaranteed |
|
|
| **Persistence** | [Android §2.2.3](./01-platform-capability-reference.md#223-alarms-cannot-be-preserved-solely-in-ram) | [§2 - Persistence Requirements](./03-plugin-requirements.md#2-persistence-requirements) | [Persistence Investigation](./02-plugin-behavior-exploration.md#13-persistence-investigation) | Phase 1 |
|
|
| **Exact alarm permission** | [Android §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree) | [§10.1.1 - Permissions](./03-plugin-requirements.md#1011-permissions) | [Test 6](./02-plugin-behavior-exploration.md#test-6-exact-alarm-permission-android-12) | Phase 1 |
|
|
|
|
---
|
|
|
|
## 10. Platform-Specific Requirements
|
|
|
|
### 10.1 Android Requirements
|
|
|
|
#### 10.1.1 Permissions
|
|
|
|
**Required Permissions**:
|
|
* `RECEIVE_BOOT_COMPLETED` - Boot receiver
|
|
* `SCHEDULE_EXACT_ALARM` - Android 12+ (API 31+) for exact alarms
|
|
* `POST_NOTIFICATIONS` - Android 13+ (API 33+) for notifications
|
|
|
|
**Permission Handling**:
|
|
* Check permission before scheduling
|
|
* Request permission if not granted
|
|
* Open system settings if permission denied
|
|
* Provide clear error messages
|
|
|
|
**Code Reference**: `DailyNotificationPlugin.kt` line 1309
|
|
|
|
**Platform Reference**: [Android §2.2.4](../01-platform-capability-reference.md#224-you-cannot-bypass-doze-or-battery-optimization-restrictions-without-permission)
|
|
|
|
---
|
|
|
|
#### 10.1.2 Manifest Entries
|
|
|
|
**Required Manifest Entries**:
|
|
```xml
|
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
|
|
<receiver
|
|
android:name="com.timesafari.dailynotification.BootReceiver"
|
|
android:enabled="true"
|
|
android:exported="true">
|
|
<intent-filter>
|
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
|
</intent-filter>
|
|
</receiver>
|
|
|
|
<receiver
|
|
android:name="com.timesafari.dailynotification.DailyNotificationReceiver"
|
|
android:enabled="true"
|
|
android:exported="false">
|
|
<intent-filter>
|
|
<action android:name="com.timesafari.daily.NOTIFICATION" />
|
|
</intent-filter>
|
|
</receiver>
|
|
```
|
|
|
|
**Location**: Test app manifest: `test-apps/android-test-app/app/src/main/AndroidManifest.xml`
|
|
|
|
---
|
|
|
|
#### 10.1.3 Notification Channels
|
|
|
|
**Required Channels**:
|
|
* `timesafari.daily` - Primary notification channel
|
|
* `daily_reminders` - Reminder notifications (if used)
|
|
|
|
**Channel Configuration**:
|
|
* Importance: HIGH (for alarms), DEFAULT (for reminders)
|
|
* Sound: Enabled by default
|
|
* Vibration: Enabled by default
|
|
* Show badge: Enabled
|
|
|
|
**Code Reference**: `ChannelManager.java` or `NotifyReceiver.kt` line 454
|
|
|
|
---
|
|
|
|
#### 10.1.4 Alarm Scheduling
|
|
|
|
**Required API Usage**:
|
|
* `setAlarmClock()` for Android 5.0+ (preferred)
|
|
* `setExactAndAllowWhileIdle()` for Android 6.0+ (fallback)
|
|
* `setExact()` for older versions (fallback)
|
|
|
|
**Code Reference**: `NotifyReceiver.kt` line 219, 223, 231
|
|
|
|
**Platform Reference**: [Android §2.1.1](../01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents)
|
|
|
|
---
|
|
|
|
### 10.2 iOS Requirements
|
|
|
|
#### 10.2.1 Permissions
|
|
|
|
**Required Permissions**:
|
|
* Notification authorization (requested at runtime)
|
|
|
|
**Permission Handling**:
|
|
* Request permission before scheduling
|
|
* Handle authorization status
|
|
* Provide clear error messages
|
|
|
|
---
|
|
|
|
#### 10.2.2 Background Tasks
|
|
|
|
**Required Background Task Identifiers**:
|
|
* `com.timesafari.dailynotification.fetch` - Background fetch
|
|
* `com.timesafari.dailynotification.notify` - Notification task (if used)
|
|
|
|
**Background Task Registration**:
|
|
* Register in `Info.plist`:
|
|
```xml
|
|
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
|
<array>
|
|
<string>com.timesafari.dailynotification.fetch</string>
|
|
</array>
|
|
```
|
|
|
|
**Code Reference**: `DailyNotificationPlugin.swift` line 31-32
|
|
|
|
**Platform Reference**: [iOS §3.1.3](../01-platform-capability-reference.md#313-background-tasks-for-prefetching)
|
|
|
|
---
|
|
|
|
#### 10.2.3 Notification Scheduling
|
|
|
|
**Required API Usage**:
|
|
* `UNUserNotificationCenter.add()` - Schedule notifications
|
|
* `UNCalendarNotificationTrigger` - Calendar-based triggers (preferred)
|
|
* `UNTimeIntervalNotificationTrigger` - Time interval triggers
|
|
|
|
**Code Reference**: `DailyNotificationScheduler.swift` line 185
|
|
|
|
**Platform Reference**: [iOS §3.1.1](../01-platform-capability-reference.md#311-notifications-survive-app-termination)
|
|
|
|
---
|
|
|
|
#### 10.2.4 Notification Categories
|
|
|
|
**Required Categories**:
|
|
* `DAILY_NOTIFICATION` - Primary notification category
|
|
|
|
**Category Configuration**:
|
|
* Actions: Configure as needed
|
|
* Options: Custom sound, custom actions
|
|
|
|
**Code Reference**: `DailyNotificationScheduler.swift` line 62+
|
|
|
|
---
|
|
|
|
## 11. Testing Requirements
|
|
|
|
### 11.1 Required Test Scenarios
|
|
|
|
The plugin **must** be tested for:
|
|
|
|
**Android**:
|
|
* [ ] Base case (alarm fires on time)
|
|
* [ ] Swipe from recents
|
|
* [ ] OS kill (memory pressure)
|
|
* [ ] Device reboot (with and without app launch)
|
|
* [ ] Force stop (with app restart)
|
|
* [ ] Exact alarm permission (Android 12+)
|
|
* [ ] Boot receiver functionality
|
|
* [ ] Missed alarm detection
|
|
|
|
**iOS**:
|
|
* [ ] Base case (notification fires on time)
|
|
* [ ] Swipe app away
|
|
* [ ] Device reboot (without app launch)
|
|
* [ ] Hard termination and relaunch
|
|
* [ ] Background execution limits
|
|
* [ ] Missed notification detection
|
|
|
|
**Cross-Platform**:
|
|
* [ ] Timezone changes
|
|
* [ ] Clock adjustments
|
|
* [ ] Multiple simultaneous alarms
|
|
* [ ] Repeating alarms
|
|
* [ ] Alarm cancellation
|
|
|
|
**Test Documentation**: See [Plugin Behavior Exploration](./02-plugin-behavior-exploration.md) for test matrices.
|
|
|
|
---
|
|
|
|
## 12. Migration Rules
|
|
|
|
### 12.1 Storage Format Changes
|
|
|
|
**If plugin storage format changes**, migration **MUST** define:
|
|
|
|
1. **Version detection**: How to identify old vs new format
|
|
2. **Safe upgrade path**: Step-by-step migration process
|
|
3. **Failure fallback**: What happens if migration fails
|
|
4. **No data loss**: All existing alarms must be preserved or explicitly marked as lost
|
|
|
|
**Migration Requirements**:
|
|
* **MUST** run automatically on app launch
|
|
* **MUST** log migration steps for debugging
|
|
* **MUST** handle partial migrations gracefully
|
|
* **MUST NOT** lose user data without explicit user confirmation
|
|
|
|
**Example Migration**:
|
|
```kotlin
|
|
// Pseudo-code
|
|
if (databaseVersion < currentVersion) {
|
|
migrateDatabase(oldVersion, currentVersion)
|
|
verifyMigrationIntegrity()
|
|
if (migrationFailed) {
|
|
rollbackToBackup()
|
|
notifyUser()
|
|
}
|
|
}
|
|
```
|
|
|
|
### 12.2 API Contract Changes
|
|
|
|
**If JS/TS API changes**:
|
|
* **MUST** maintain backward compatibility for at least one major version
|
|
* **MUST** provide deprecation warnings before removal
|
|
* **MUST** document migration path in changelog
|
|
* **MUST** update Doc C before releasing breaking changes
|
|
|
|
---
|
|
|
|
## 13. API Stability Guarantees
|
|
|
|
### 13.1 Breaking Change Policy
|
|
|
|
**No breaking change may occur without**:
|
|
|
|
1. **Doc C update** - Requirements document updated first
|
|
2. **MINOR or MAJOR version bump** - Semantic versioning enforced
|
|
3. **Migration notes** - Clear upgrade path documented
|
|
4. **Deprecation period** - Old API deprecated for at least one release cycle
|
|
|
|
### 13.2 Stability Guarantees
|
|
|
|
**Plugin guarantees**:
|
|
* **MUST** maintain API contract within same major version
|
|
* **SHOULD** maintain API contract across minor versions (exceptions documented)
|
|
* **MUST NOT** break API contract without major version bump
|
|
|
|
**Example**:
|
|
* `v1.0.0` → `v1.1.0`: New features added, no breaking changes ✅
|
|
* `v1.1.0` → `v2.0.0`: Breaking API change, migration guide required ✅
|
|
* `v1.1.0` → `v1.2.0`: Breaking API change ❌ (must be v2.0.0)
|
|
|
|
---
|
|
|
|
## 14. "What Happens If We Do Nothing?" Analysis
|
|
|
|
### 14.1 No Recovery Implementation
|
|
|
|
**If plugin does NOT implement recovery**:
|
|
|
|
| Scenario | What Happens | Impact |
|
|
| -------- | ------------ | ------ |
|
|
| **Device Reboot** (Android) | All alarms wiped, never fire | **Critical**: User loses all scheduled alarms |
|
|
| **Force Stop** (Android) | All alarms cleared, never fire until app opened | **Critical**: User loses alarms until manual app launch |
|
|
| **Cold Start** | Missed alarms never detected | **Major**: User unaware of missed notifications |
|
|
| **Warm Start** | Alarms may be missing, never verified | **Minor**: Edge case, alarms usually still work |
|
|
|
|
**Conclusion**: Recovery **MUST** be implemented for Android reboot and force stop scenarios. Cold start recovery **SHOULD** be implemented for user experience.
|
|
|
|
### 14.2 No Persistence
|
|
|
|
**If plugin does NOT persist alarms**:
|
|
|
|
| Scenario | What Happens | Impact |
|
|
| -------- | ------------ | ------ |
|
|
| **App Kill** | All alarms lost | **Critical**: User loses all scheduled alarms |
|
|
| **Reboot** | Cannot reschedule | **Critical**: Recovery impossible |
|
|
| **Force Stop** | Cannot detect missed alarms | **Major**: Recovery incomplete |
|
|
|
|
**Conclusion**: Persistence **MUST** be implemented. It is foundational for all recovery scenarios.
|
|
|
|
### 14.3 No Missed Alarm Detection
|
|
|
|
**If plugin does NOT detect missed alarms**:
|
|
|
|
| Scenario | What Happens | Impact |
|
|
| -------- | ------------ | ------ |
|
|
| **Missed Alarm** | User never notified | **Major**: User unaware of missed notifications |
|
|
| **Repeating Alarm** | Next occurrence may be wrong | **Major**: Schedule drift over time |
|
|
|
|
**Conclusion**: Missed alarm detection **SHOULD** be implemented for user experience and schedule accuracy.
|
|
|
|
---
|
|
|
|
## 15. Versioning Requirements
|
|
|
|
### 15.1 Breaking Changes
|
|
|
|
Any change to alarm behavior is **breaking** and requires:
|
|
|
|
* **MAJOR version bump** (semantic versioning)
|
|
* **Migration guide** for existing users
|
|
* **Deprecation warnings** (if applicable)
|
|
* **Clear changelog entry**
|
|
|
|
### 10.2 Non-Breaking Changes
|
|
|
|
Non-breaking changes include:
|
|
* Bug fixes
|
|
* Performance improvements
|
|
* Additional features (backward compatible)
|
|
* Documentation updates
|
|
|
|
---
|
|
|
|
## 16. Cross-Reference Anchors
|
|
|
|
**All cross-references use standardized anchors**:
|
|
|
|
| Reference Format | Example | Resolves To |
|
|
| ---------------- | ------- | ----------- |
|
|
| `[Doc A §X.Y]` | `[Doc A §2.1.1]` | [Platform Reference §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents) |
|
|
| `[Doc B §X.Y]` | `[Doc B §1.2]` | [Exploration §1.2](./02-plugin-behavior-exploration.md#12-behavior-testing-matrix) |
|
|
| `[Doc C §X.Y]` | `[Doc C §3.1.2]` | [Requirements §3.1.2](#312-app-cold-start) |
|
|
| `[Phase N §X.Y]` | `[Phase 1 §2.3]` | [Phase 1 Implementation](../android-implementation-directive-phase1.md#23-cold-start-recovery) |
|
|
|
|
**Anchor Rules**:
|
|
* All section headers MUST have unique anchors
|
|
* Cross-references MUST use exact anchor format
|
|
* Broken links MUST be fixed immediately
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- [Platform Capability Reference](./01-platform-capability-reference.md) - OS-level facts
|
|
- [Plugin Behavior Exploration](./02-plugin-behavior-exploration.md) - Exploration template
|
|
- [Unified Alarm Directive](./000-UNIFIED-ALARM-DIRECTIVE.md) - Master coordination document
|
|
- [Phase 1: Cold Start Recovery](../android-implementation-directive-phase1.md) - Implementation
|
|
- [Phase 2: Force Stop Recovery](../android-implementation-directive-phase2.md) - Implementation
|
|
- [Phase 3: Boot Recovery](../android-implementation-directive-phase3.md) - Implementation
|
|
|
|
---
|
|
|
|
## Version History
|
|
|
|
- **v1.1.0** (November 2025): Enhanced with non-guarantees, failure modes, storage schema
|
|
- Added "Plugin WILL NOT Guarantee" section
|
|
- Added Guarantee vs Best-Effort matrix
|
|
- Added Failure Modes & Required Responses table
|
|
- Added Explicit Storage Schema
|
|
- Enhanced JS/TS API contract with returned fields and error states
|
|
- Added Missed Alarm Resolution Rules
|
|
- Added State Transition Table
|
|
- Added Explicit Unsupported Features List
|
|
|
|
- **v1.0.0** (November 2025): Initial requirements document
|
|
- Plugin behavior guarantees and limitations
|
|
- Persistence requirements
|
|
- Recovery requirements
|
|
- Missed alarm handling contract
|
|
- JS/TS API contract
|
|
- Platform-specific requirements
|
|
- Testing requirements
|
|
- Traceability matrix
|
|
|