Adds complete iOS documentation suite to support iOS implementation parity with Android features. Includes implementation directives, recovery scenario mappings, database migration guide, troubleshooting guide, and test scripts. New Documentation: - iOS Implementation Directive: Phase-based implementation guide mirroring Android structure with iOS-specific considerations - iOS Recovery Scenario Mapping: Maps Android recovery scenarios to iOS equivalents with detection logic comparisons - iOS Core Data Migration Guide: Complete Room → Core Data entity mappings with implementation checklist for missing entities - iOS Troubleshooting Guide: Common issues, debugging techniques, and error code reference Enhanced Documentation: - API.md: Added iOS-only methods (permissions, background tasks, pending notifications), platform differences table, and iOS-specific error types. Updated version to 2.3.0. Test Infrastructure: - iOS test scripts for Phase 1 (cold start), Phase 2 (termination), and Phase 3 (boot recovery) testing scenarios All documentation addresses gaps identified in iOS Implementation Documentation Review and provides foundation for iOS recovery feature implementation (currently pending). Note: iOS recovery features (ReactivationManager, scenario detection) are NOT yet implemented. Documentation is ready to guide implementation.
16 KiB
TimeSafari Daily Notification Plugin API Reference
Author: Matthew Raymer
Version: 2.3.0
Last Updated: 2025-12-08
Overview
This API reference provides comprehensive documentation for the TimeSafari Daily Notification Plugin, optimized for native-first architecture supporting Android, iOS, and Electron platforms.
Platform Support
- ✅ Android: WorkManager + AlarmManager + SQLite
- ✅ iOS: BGTaskScheduler + UNUserNotificationCenter + Core Data
- ✅ Electron: Desktop notifications + SQLite/LocalStorage
- ❌ Web (PWA): Removed for native-first focus
DailyNotificationPlugin Interface
Configuration
configure(options: ConfigureOptions): Promise<void>
Configure the plugin with storage, TTL, and optimization settings.
Parameters:
options.storage:'shared'|'tiered'- Storage modeoptions.ttlSeconds:number- TTL in seconds (default: 1800)options.prefetchLeadMinutes:number- Prefetch lead time (default: 15)options.enableETagSupport:boolean- Enable ETag conditional requestsoptions.enableErrorHandling:boolean- Enable advanced error handlingoptions.enablePerformanceOptimization:boolean- Enable performance optimization
Core Methods
scheduleDailyNotification(options: NotificationOptions): Promise<void>
Schedule a daily notification with content fetching.
Parameters:
options.url:string- Content endpoint URLoptions.time:string- Time in HH:MM formatoptions.title:string- Notification titleoptions.body:string- Notification bodyoptions.sound:boolean- Enable sound (optional)options.retryConfig:RetryConfiguration- Custom retry settings (optional)
getLastNotification(): Promise<NotificationResponse | null>
Get the last scheduled notification.
cancelAllNotifications(): Promise<void>
Cancel all scheduled notifications.
Platform-Specific Methods
Android Only
getExactAlarmStatus(): Promise<ExactAlarmStatus>
Get exact alarm permission and capability status.
requestExactAlarmPermission(): Promise<void>
Request exact alarm permission from user.
openExactAlarmSettings(): Promise<void>
Open exact alarm settings in system preferences.
getRebootRecoveryStatus(): Promise<RecoveryStatus>
Get reboot recovery status and statistics.
isAlarmScheduled(options: { triggerAtMillis: number }): Promise<{ scheduled: boolean; triggerAtMillis: number }>
Check if an alarm is scheduled for a specific trigger time. Useful for debugging and verification.
Parameters:
options.triggerAtMillis:number- The trigger time in milliseconds (Unix timestamp)
Returns:
scheduled:boolean- Whether the alarm is currently scheduledtriggerAtMillis:number- The trigger time that was checked
Example:
const result = await DailyNotification.isAlarmScheduled({
triggerAtMillis: 1762421400000
});
console.log(`Alarm scheduled: ${result.scheduled}`);
getNextAlarmTime(): Promise<{ scheduled: boolean; triggerAtMillis?: number }>
Get the next scheduled alarm time from AlarmManager. Requires Android 5.0+ (API 21+).
Returns:
scheduled:boolean- Whether any alarm is scheduledtriggerAtMillis:number | undefined- The next alarm trigger time (if scheduled)
Example:
const result = await DailyNotification.getNextAlarmTime();
if (result.scheduled) {
const nextAlarm = new Date(result.triggerAtMillis);
console.log(`Next alarm: ${nextAlarm.toLocaleString()}`);
}
testAlarm(options?: { secondsFromNow?: number }): Promise<{ scheduled: boolean; secondsFromNow: number; triggerAtMillis: number }>
Schedule a test alarm that fires in a few seconds. Useful for verifying alarm delivery works correctly.
Parameters:
options.secondsFromNow:number(optional) - Seconds from now to fire the alarm (default: 5)
Returns:
scheduled:boolean- Whether the alarm was scheduled successfullysecondsFromNow:number- The delay usedtriggerAtMillis:number- The trigger time in milliseconds
Example:
const result = await DailyNotification.testAlarm({ secondsFromNow: 10 });
console.log(`Test alarm scheduled for ${result.secondsFromNow} seconds`);
iOS Only
getNotificationPermissionStatus(): Promise<NotificationPermissionStatus>
Get notification permission status on iOS. Required before scheduling notifications.
Returns:
authorized:boolean- Whether notifications are authorizeddenied:boolean- Whether notifications are deniednotDetermined:boolean- Whether permission hasn't been requested yetprovisional:boolean- Whether provisional authorization is granted (iOS 12+)
Example:
const status = await DailyNotification.getNotificationPermissionStatus();
if (!status.authorized) {
await DailyNotification.requestNotificationPermission();
}
requestNotificationPermission(): Promise<{ granted: boolean }>
Request notification permission from user. Must be called before scheduling notifications.
Returns:
granted:boolean- Whether permission was granted
Example:
const result = await DailyNotification.requestNotificationPermission();
if (result.granted) {
await DailyNotification.scheduleDailyNotification({ ... });
}
getPendingNotifications(): Promise<{ count: number; notifications: PendingNotification[] }>
Get all pending notifications from UNUserNotificationCenter. Useful for debugging and verification.
Returns:
count:number- Number of pending notificationsnotifications:PendingNotification[]- Array of pending notification details
Example:
const result = await DailyNotification.getPendingNotifications();
console.log(`Pending notifications: ${result.count}`);
result.notifications.forEach(notif => {
console.log(`Notification: ${notif.identifier} at ${notif.triggerDate}`);
});
getBackgroundTaskStatus(): Promise<BackgroundTaskStatus>
Get background task registration and execution status. Useful for debugging background prefetch.
Returns:
fetchTaskRegistered:boolean- Whether fetch background task is registerednotifyTaskRegistered:boolean- Whether notify background task is registeredlastFetchExecution:number | null- Last fetch execution time (epoch ms)lastNotifyExecution:number | null- Last notify execution time (epoch ms)backgroundRefreshEnabled:boolean- Whether Background App Refresh is enabled
Example:
const status = await DailyNotification.getBackgroundTaskStatus();
if (!status.backgroundRefreshEnabled) {
console.warn('Background App Refresh is disabled. Enable in Settings.');
}
openNotificationSettings(): Promise<void>
Open notification settings in iOS Settings app. Useful for guiding users to enable notifications.
Example:
await DailyNotification.openNotificationSettings();
openBackgroundAppRefreshSettings(): Promise<void>
Open Background App Refresh settings in iOS Settings app. Useful for guiding users to enable background execution.
Example:
await DailyNotification.openBackgroundAppRefreshSettings();
Management Methods
maintainRollingWindow(): Promise<void>
Manually trigger rolling window maintenance.
getRollingWindowStats(): Promise<RollingWindowStats>
Get rolling window statistics and status.
Optimization Methods
optimizeDatabase(): Promise<void>
Optimize database performance with indexes and settings.
optimizeMemory(): Promise<void>
Optimize memory usage and perform cleanup.
optimizeBattery(): Promise<void>
Optimize battery usage and background CPU.
Metrics and Monitoring
getPerformanceMetrics(): Promise<PerformanceMetrics>
Get comprehensive performance metrics.
getErrorMetrics(): Promise<ErrorMetrics>
Get error handling metrics and statistics.
getNetworkMetrics(): Promise<NetworkMetrics>
Get network efficiency metrics (ETag support).
getMemoryMetrics(): Promise<MemoryMetrics>
Get memory usage metrics and statistics.
getObjectPoolMetrics(): Promise<ObjectPoolMetrics>
Get object pooling efficiency metrics.
Utility Methods
resetPerformanceMetrics(): Promise<void>
Reset all performance metrics to zero.
resetErrorMetrics(): Promise<void>
Reset error handling metrics.
clearRetryStates(): Promise<void>
Clear all retry states and operations.
cleanExpiredETags(): Promise<void>
Clean expired ETag cache entries.
Data Types
ConfigureOptions
interface ConfigureOptions {
storage?: 'shared' | 'tiered';
ttlSeconds?: number;
prefetchLeadMinutes?: number;
enableETagSupport?: boolean;
enableErrorHandling?: boolean;
enablePerformanceOptimization?: boolean;
maxRetries?: number;
baseRetryDelay?: number;
maxRetryDelay?: number;
backoffMultiplier?: number;
memoryWarningThreshold?: number;
memoryCriticalThreshold?: number;
objectPoolSize?: number;
maxObjectPoolSize?: number;
}
NotificationOptions
interface NotificationOptions {
url: string;
time: string;
title: string;
body: string;
sound?: boolean;
retryConfig?: RetryConfiguration;
}
ExactAlarmStatus (Android)
interface ExactAlarmStatus {
supported: boolean;
enabled: boolean;
canSchedule: boolean;
fallbackWindow: string;
}
NotificationPermissionStatus (iOS)
interface NotificationPermissionStatus {
authorized: boolean;
denied: boolean;
notDetermined: boolean;
provisional: boolean; // iOS 12+
}
PendingNotification (iOS)
interface PendingNotification {
identifier: string;
title: string;
body: string;
triggerDate: number; // epoch ms
triggerType: 'calendar' | 'timeInterval' | 'location';
repeats: boolean;
}
BackgroundTaskStatus (iOS)
interface BackgroundTaskStatus {
fetchTaskRegistered: boolean;
notifyTaskRegistered: boolean;
lastFetchExecution: number | null; // epoch ms
lastNotifyExecution: number | null; // epoch ms
backgroundRefreshEnabled: boolean;
}
PerformanceMetrics
interface PerformanceMetrics {
overallScore: number;
databasePerformance: number;
memoryEfficiency: number;
batteryEfficiency: number;
objectPoolEfficiency: number;
totalDatabaseQueries: number;
averageMemoryUsage: number;
objectPoolHits: number;
backgroundCpuUsage: number;
totalNetworkRequests: number;
recommendations: string[];
}
ErrorMetrics
interface ErrorMetrics {
totalErrors: number;
networkErrors: number;
storageErrors: number;
schedulingErrors: number;
permissionErrors: number;
configurationErrors: number;
systemErrors: number;
unknownErrors: number;
cacheHitRatio: number;
}
Error Handling
All methods return promises that reject with descriptive error messages. The plugin includes comprehensive error categorization and retry logic.
Common Error Types
- Network Errors: Connection timeouts, DNS failures
- Storage Errors: Database corruption, disk full
- Permission Errors: Missing exact alarm permission (Android) or notification permission (iOS)
- Configuration Errors: Invalid parameters, unsupported settings
- System Errors: Out of memory, platform limitations
Platform-Specific Errors
Android
EXACT_ALARM_PERMISSION_DENIED: User denied exact alarm permissionBOOT_RECEIVER_NOT_REGISTERED: Boot receiver not properly registeredALARM_MANAGER_UNAVAILABLE: AlarmManager service unavailable
iOS
NOTIFICATION_PERMISSION_DENIED: User denied notification permissionBACKGROUND_REFRESH_DISABLED: Background App Refresh disabled in SettingsPENDING_NOTIFICATION_LIMIT_EXCEEDED: Exceeded 64 notification limitBG_TASK_NOT_REGISTERED: Background task not registered in Info.plistBG_TASK_EXECUTION_FAILED: Background task execution failed
Platform Differences
Android
- Requires
SCHEDULE_EXACT_ALARMpermission for precise timing - Falls back to windowed alarms (±10m) if exact permission denied
- Supports reboot recovery with broadcast receivers
- Full performance optimization features
- Alarms do NOT persist across reboot (must reschedule)
- Force stop clears all alarms (cannot bypass)
- App code CAN run when alarm fires (via PendingIntent)
iOS
- Uses
BGTaskSchedulerfor background prefetch - Uses
UNUserNotificationCenterfor notification scheduling - Limited to 64 pending notifications (OS constraint)
- Automatic background task management
- Battery optimization built-in
- Notifications persist across app termination and reboot (OS-guaranteed)
- App code does NOT run when notification fires (only if user taps)
- ±180 second timing tolerance for calendar-based notifications
- Background execution severely limited (BGTaskScheduler only, system-controlled)
- No user-facing "force stop" equivalent
- Must request notification permission before scheduling
Key Differences Summary
| Feature | Android | iOS |
|---|---|---|
| Notification Persistence | ❌ Must reschedule after reboot | ✅ Automatic (OS-guaranteed) |
| Code Execution on Fire | ✅ Yes (PendingIntent) | ❌ No (only if user taps) |
| Background Execution | ✅ WorkManager, JobScheduler | ⚠️ Limited (BGTaskScheduler) |
| Timing Accuracy | ✅ Exact (with permission) | ⚠️ ±180 seconds tolerance |
| Force Stop | ✅ User-facing option | ❌ No equivalent |
| Boot Recovery | ✅ Must implement | ✅ Automatic (notifications persist) |
| Permission Model | ✅ Runtime permission | ✅ Runtime permission |
| Pending Limit | ✅ No limit | ❌ 64 notifications max |
Electron
- Desktop notification support
- SQLite or LocalStorage fallback
- Native desktop notification APIs
- Cross-platform desktop compatibility
TimeSafari-Specific Integration Examples
Basic TimeSafari Integration
import { DailyNotification } from '@timesafari/daily-notification-plugin';
import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin';
// Initialize TimeSafari integration
const integrationService = TimeSafariIntegrationService.getInstance();
// Configure with TimeSafari-specific settings
await integrationService.initialize({
activeDid: 'did:example:timesafari-user-123',
storageAdapter: timeSafariStorageAdapter,
endorserApiBaseUrl: 'https://endorser.ch/api/v1'
});
// Schedule TimeSafari community notifications
await DailyNotification.scheduleDailyNotification({
title: 'New Community Update',
body: 'You have new offers and project updates',
time: '09:00',
channel: 'timesafari_community_updates'
});
TimeSafari Community Features
// Fetch community data with rate limiting
const communityService = new TimeSafariCommunityIntegrationService();
await communityService.initialize({
maxRequestsPerMinute: 30,
maxRequestsPerHour: 1000,
basePollingIntervalMs: 300000, // 5 minutes
adaptivePolling: true
});
// Fetch community data
const bundle = await communityService.fetchCommunityDataWithRateLimit({
activeDid: 'did:example:timesafari-user-123',
fetchOffersToPerson: true,
fetchOffersToProjects: true,
fetchProjectUpdates: true,
starredPlanIds: ['plan-1', 'plan-2', 'plan-3']
});
DID-Signed Payloads
// Generate DID-signed notification payloads
const samplePayloads = integrationService.generateSampleDidPayloads();
for (const payload of samplePayloads) {
// Verify signature
const isValid = await integrationService.verifyDidSignature(
JSON.stringify(payload.payload),
payload.signature
);
console.log(`Payload ${payload.type} signature valid: ${isValid}`);
}
Privacy-Preserving Storage
// Configure privacy-preserving storage
const storageAdapter = new TimeSafariStorageAdapterImpl(
nativeStorage,
'timesafari_notifications'
);
// Store with TTL and redaction
await storageAdapter.store('community_data', bundle, 3600); // 1 hour TTL
// Get data retention policy
const policy = integrationService.getDataRetentionPolicy();
console.log('Data retention policy:', policy);