Files
daily-notification-plugin/API.md
Matthew dd8d67462f docs(ios): add comprehensive iOS implementation documentation
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.
2025-12-08 23:36:30 -08:00

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 mode
  • options.ttlSeconds: number - TTL in seconds (default: 1800)
  • options.prefetchLeadMinutes: number - Prefetch lead time (default: 15)
  • options.enableETagSupport: boolean - Enable ETag conditional requests
  • options.enableErrorHandling: boolean - Enable advanced error handling
  • options.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 URL
  • options.time: string - Time in HH:MM format
  • options.title: string - Notification title
  • options.body: string - Notification body
  • options.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 scheduled
  • triggerAtMillis: 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 scheduled
  • triggerAtMillis: 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 successfully
  • secondsFromNow: number - The delay used
  • triggerAtMillis: 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 authorized
  • denied: boolean - Whether notifications are denied
  • notDetermined: boolean - Whether permission hasn't been requested yet
  • provisional: 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 notifications
  • notifications: 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 registered
  • notifyTaskRegistered: boolean - Whether notify background task is registered
  • lastFetchExecution: 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 permission
  • BOOT_RECEIVER_NOT_REGISTERED: Boot receiver not properly registered
  • ALARM_MANAGER_UNAVAILABLE: AlarmManager service unavailable

iOS

  • NOTIFICATION_PERMISSION_DENIED: User denied notification permission
  • BACKGROUND_REFRESH_DISABLED: Background App Refresh disabled in Settings
  • PENDING_NOTIFICATION_LIMIT_EXCEEDED: Exceeded 64 notification limit
  • BG_TASK_NOT_REGISTERED: Background task not registered in Info.plist
  • BG_TASK_EXECUTION_FAILED: Background task execution failed

Platform Differences

Android

  • Requires SCHEDULE_EXACT_ALARM permission 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 BGTaskScheduler for background prefetch
  • Uses UNUserNotificationCenter for 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);