48 KiB
iOS Android Parity Directive — iOS Implementation Upgrade
Status: 🎯 PLANNING - Directive for iOS-2 branch
Date: 2025-11-13
Author: Matthew Raymer
Branch: ios-2
Objective: Upgrade iOS implementation to match Android functionality while preserving Android code and TypeScript interface
Executive Summary
This directive outlines the plan to upgrade the iOS implementation of the Daily Notification Plugin to achieve feature parity with the functioning Android implementation. The Android codebase and TypeScript interface definitions will remain completely untouched during this work.
Key Constraints
- ✅ Android code must remain unchanged - No modifications to
src/android/directory - ✅ TypeScript interface must remain unchanged - No modifications to
src/definitions.ts - ✅ iOS implementation must match Android functionality - All Android
@PluginMethodmethods must have iOS equivalents - ✅ Platform-specific adaptations allowed - iOS can use Swift/iOS APIs but must provide same functionality
Testing & Scope
- ✅ Primary Testing Target: iOS Simulator
- ✅ Initial Scope: One scheduled prefetch + one scheduled notification per day
- Prefetch Timing: 5 minutes before notification time
- Notification Timing: User-specified daily time
- ✅ Test App:
ios-test-app(equivalent toandroid-test-app) with same HTML/JS UI - ✅ Rolling Window: Deferred to Phase 2 (initial implementation supports single daily schedule)
Readiness Milestones
- Phase 1: Test app ready for basic testing (one prefetch + one notification per day)
- Phase 3: Library ready for TimeSafari integration
- Phase 4: Full library ready for production TimeSafari integration
Minimal Viable Parity Definition
Objective: Define the exact minimal scope iOS must meet before Android parity is considered achieved for Phase 1.
Required Method Implementations (Phase 1)
configure()- Plugin configuration with database/storage optionsscheduleDailyNotification()- Schedule single daily notification (one prefetch + one notification)getLastNotification()- Get last delivered notificationcancelAllNotifications()- Cancel all scheduled notificationsgetNotificationStatus()- Get current notification statusupdateSettings()- Update notification settings
Required Background Task Behavior
- Prefetch Task: Scheduled 5 minutes before notification time via BGTaskScheduler
- Notification: Scheduled at user-specified time via UNUserNotificationCenter
- Fallback: If BGTaskScheduler task not run within 15 minutes of
earliestBeginDate, trigger reschedule - Error Handling: Return same error codes as Android (see Error Code Matching section)
Required Durability Guarantees
- Storage: Content cached in UserDefaults or CoreData
- Persistence: Schedule survives app termination
- Recovery: On app launch, reschedule today's schedule from persisted settings
- Full reboot detection and status reporting is introduced in Phase 2
- Phase 1 uses simple "reschedule on launch" approach
Required Error Handling Surface
- All methods must return same error keys as Android
- All methods must return same JSON shape as Android
- All methods must log using same categories as Android
Required JSON Return Structure
Each method must match Android's return structure exactly:
- Same field names
- Same data types
- Same nullability rules
- Same error response format
Current State Assessment
Android Implementation (Reference)
Location: src/android/DailyNotificationPlugin.java
Key Components:
- ✅
DailyNotificationPlugin.java- Main plugin class (1936 lines) - ✅
DailyNotificationStorage.java- Storage abstraction - ✅
DailyNotificationScheduler.java- AlarmManager-based scheduling - ✅
DailyNotificationFetcher.java- Background content fetching - ✅
DailyNotificationDatabase.java- SQLite database management - ✅
DailyNotificationRollingWindow.java- Rolling window safety - ✅
DailyNotificationExactAlarmManager.java- Exact alarm management - ✅
DailyNotificationRebootRecoveryManager.java- Reboot recovery - ✅
DailyNotificationTTLEnforcer.java- TTL enforcement - ✅
DailyNotificationETagManager.java- ETag caching - ✅
DailyNotificationJWTManager.java- JWT authentication - ✅
EnhancedDailyNotificationFetcher.java- TimeSafari API integration - ✅
DailyNotificationFetchWorker.java- WorkManager background worker - ✅
DailyNotificationReceiver.java- Broadcast receiver for alarms
Implemented Methods (from Android):
configure()- Plugin configuration with database/storage optionsscheduleDailyNotification()- Schedule daily notificationgetLastNotification()- Get last delivered notificationcancelAllNotifications()- Cancel all scheduled notificationsgetNotificationStatus()- Get current notification statusupdateSettings()- Update notification settingsgetBatteryStatus()- Get battery status informationrequestBatteryOptimizationExemption()- Request battery optimization exemptionsetAdaptiveScheduling()- Enable/disable adaptive schedulinggetPowerState()- Get current power statemaintainRollingWindow()- Manual rolling window maintenancegetRollingWindowStats()- Get rolling window statisticsgetExactAlarmStatus()- Get exact alarm statusrequestExactAlarmPermission()- Request exact alarm permissionopenExactAlarmSettings()- Open exact alarm settingsgetRebootRecoveryStatus()- Get reboot recovery statussetActiveDidFromHost()- Set activeDid from host (Phase 1)refreshAuthenticationForNewIdentity()- Refresh authentication (Phase 1)clearCacheForNewIdentity()- Clear cache for new identity (Phase 1)updateBackgroundTaskIdentity()- Update background task identity (Phase 1)testJWTGeneration()- Test JWT generation (Phase 1)testEndorserAPI()- Test Endorser.ch API (Phase 1)coordinateBackgroundTasks()- Coordinate background tasks (Phase 3)handleAppLifecycleEvent()- Handle app lifecycle events (Phase 3)getCoordinationStatus()- Get coordination status (Phase 3)scheduleDailyReminder()- Schedule static daily remindercancelDailyReminder()- Cancel daily remindergetScheduledReminders()- Get all scheduled remindersupdateDailyReminder()- Update existing daily reminder
iOS Implementation (Current)
Location: ios/Plugin/DailyNotificationPlugin.swift
Current Components:
- ✅
DailyNotificationPlugin.swift- Main plugin class (480 lines, partial) - ✅
DailyNotificationDatabase.swift- CoreData database (exists) - ✅
DailyNotificationRollingWindow.swift- Rolling window (exists) - ✅
DailyNotificationTTLEnforcer.swift- TTL enforcer (exists) - ✅
DailyNotificationETagManager.swift- ETag manager (exists) - ✅
DailyNotificationBackgroundTaskManager.swift- Background task manager (exists) - ✅
DailyNotificationConstants.swift- Constants (exists) - ✅
DailyNotificationErrorHandler.swift- Error handling (exists) - ✅
DailyNotificationLogger.swift- Logging (exists) - ✅
DailyNotificationPowerManager.swift- Power management (exists) - ✅
DailyNotificationPerformanceOptimizer.swift- Performance (exists) - ✅
DailyNotificationCallbacks.swift- Callbacks (exists) - ✅
DailyNotificationConfig.swift- Configuration (exists) - ✅
DailyNotificationModel.swift- Data models (exists) - ✅
DailyNotificationMaintenanceWorker.swift- Maintenance (exists)
Currently Implemented Methods:
- ✅
configure()- Basic configuration (needs enhancement) - ✅
scheduleContentFetch()- Basic BGTaskScheduler (needs enhancement) - ✅
scheduleUserNotification()- Basic UNUserNotificationCenter (needs enhancement) - ✅
scheduleDualNotification()- Dual scheduling (needs enhancement) - ✅
getDualScheduleStatus()- Status retrieval (needs enhancement) - ✅
scheduleDailyReminder()- Static reminders (implemented) - ✅
cancelDailyReminder()- Cancel reminders (implemented) - ✅
getScheduledReminders()- Get reminders (implemented) - ✅
updateDailyReminder()- Update reminders (implemented)
Missing Methods (Need Implementation):
- ❌
scheduleDailyNotification()- Main scheduling method - ❌
getLastNotification()- Last notification retrieval - ❌
cancelAllNotifications()- Cancel all notifications - ❌
getNotificationStatus()- Status retrieval - ❌
updateSettings()- Settings update - ❌
getBatteryStatus()- Battery status - ❌
requestBatteryOptimizationExemption()- Battery optimization - ❌
setAdaptiveScheduling()- Adaptive scheduling - ❌
getPowerState()- Power state - ❌
maintainRollingWindow()- Rolling window maintenance - ❌
getRollingWindowStats()- Rolling window stats - ❌
getExactAlarmStatus()- Exact alarm status (iOS equivalent) - ❌
requestExactAlarmPermission()- Alarm permission (iOS equivalent) - ❌
openExactAlarmSettings()- Alarm settings (iOS equivalent) - ❌
getRebootRecoveryStatus()- Reboot recovery status - ❌
setActiveDidFromHost()- ActiveDid management (Phase 1) - ❌
refreshAuthenticationForNewIdentity()- Auth refresh (Phase 1) - ❌
clearCacheForNewIdentity()- Cache clearing (Phase 1) - ❌
updateBackgroundTaskIdentity()- Background task identity (Phase 1) - ❌
testJWTGeneration()- JWT testing (Phase 1) - ❌
testEndorserAPI()- API testing (Phase 1) - ❌
coordinateBackgroundTasks()- Background coordination (Phase 3) - ❌
handleAppLifecycleEvent()- Lifecycle events (Phase 3) - ❌
getCoordinationStatus()- Coordination status (Phase 3)
Implementation Strategy
Testing Environment: iOS Simulator (primary testing target)
Initial Scope: Simplified daily schedule
- One scheduled prefetch per day (via BGTaskScheduler)
- Timing: 5 minutes before notification time
- One scheduled notification per day (via UNUserNotificationCenter)
- Timing: User-specified daily time
- Full rolling window and advanced features deferred to later phases
Phase 1: Core Infrastructure Parity
Objective: Implement core notification scheduling with single daily schedule (one prefetch + one notification).
Deliverables:
- Storage layer (UserDefaults/CoreData) with content caching
- Scheduler (UNUserNotificationCenter) with timing tolerance (±180s) and permission auto-healing
- Background fetching (BGTaskScheduler) with fallback rescheduling (15 min window)
- Core methods:
configure(),scheduleDailyNotification(),getLastNotification(),cancelAllNotifications(),getNotificationStatus(),updateSettings()
Key Constraints:
- Phase 1 uses dummy/static content fetcher (no JWT/ETag - deferred to Phase 3)
- Timing tolerance: iOS notifications may drift up to 180 seconds
- BGTaskScheduler fallback: Auto-reschedule if task missed within 15 min window (see BGTask Miss Detection below)
- Concurrency: Use Swift
actoror serial queue for DB/cache access (see Concurrency & Reentrancy Rules section)
Permission Auto-Healing:
On every call to scheduleDailyNotification():
- Read current
UNUserNotificationCenter.getNotificationSettings() - If
authorizationStatus == .denied:- Return error:
{ error: "notifications_denied", message: "Notification permissions denied" } - Log:
[DNP-PLUGIN] notifications_denied for scheduleDailyNotification - DO NOT schedule (prevents ghost schedules)
- Return error:
- If
authorizationStatus == .notDetermined:- Option A (test app): Call
requestAuthorization()then retry once - Option B (library): Fail fast with
notifications_deniedand let host request permissions
- Option A (test app): Call
- Never silently "succeed" when notifications are denied
BGTask Miss Detection:
On every app launch and every plugin entry point that touches scheduling:
- Read the last scheduled BGTask
earliestBeginDatefrom storage - Read the last successful run timestamp from storage
- If
now > earliestBeginDate + 15 minutesand no successful run timestamp is recorded, treat the task as "missed":- Log:
[DNP-FETCH] BGTask missed window; rescheduling - Schedule a new BGTask with
earliestBeginDate = now + 1 minute - Update stored
earliestBeginDatein storage
- Log:
Successful Run Definition:
A "successful run" is defined as: BGTask handler invoked, content fetch completed (even if 304 Not Modified), and next schedule successfully submitted; only then update lastSuccessfulRunTimestamp. Do not set the timestamp on handler entry or partial completion.
Completion Criteria:
- All Phase 1 methods implemented and tested
- Single daily schedule working (prefetch 5 min before notification)
- Test app ready for iOS Simulator testing
Phase 2: Advanced Features Parity
Objective: Implement rolling window, TTL enforcement, exact alarm equivalent, reboot recovery, and power management.
Deliverables:
- Rolling window (expand beyond single daily schedule, enforce iOS 64 limit)
- TTL enforcement (check at notification fire time, discard stale content)
- Exact alarm equivalent (UNCalendarNotificationTrigger with tolerance, document iOS constraints)
- Reboot recovery (uptime comparison strategy, auto-reschedule on app launch)
- Power management (battery status, Background App Refresh status)
Key Constraints:
- iOS cannot guarantee exact delivery like Android's
setExact(); TimeSafari must treat ±180s as the design target, not a bug - iOS notifications are "best effort" not "exact" (may drift, batch, or delay)
- Rolling window must respect iOS 64 pending notification limit
- Reboot detection via system uptime comparison (no BOOT_COMPLETED equivalent)
Completion Criteria:
- Rolling window functional with iOS limits enforced
- TTL validation working at notification delivery
- Reboot recovery tested and working
- Test app ready for advanced feature testing
Phase 3: TimeSafari Integration Parity
Objective: Implement JWT authentication, ETag caching, and TimeSafari API integration.
Deliverables:
- JWT management (ES256K signing, token generation/caching, refresh logic)
- ETag management (conditional requests, caching, invalidation)
- Enhanced fetcher (TimeSafari API integration, activeDid support)
- Integration methods:
setActiveDidFromHost(),refreshAuthenticationForNewIdentity(),clearCacheForNewIdentity(),updateBackgroundTaskIdentity(),testJWTGeneration(),testEndorserAPI()
Key Constraints:
- Replace Phase 1 dummy fetcher with JWT-signed fetcher
- Use same JWT algorithm as Android (ES256K)
- ETag validation must match Android behavior
Completion Criteria:
- JWT authentication working with TimeSafari API
- ETag caching reducing unnecessary network requests
- Library ready for TimeSafari integration
- Test app ready for integration testing
Phase 4: Background Coordination Parity
Objective: Implement background task coordination with TimeSafari PlatformServiceMixin.
Deliverables:
- Background coordination (
coordinateBackgroundTasks()method) - Lifecycle management (
handleAppLifecycleEvent()for app background/foreground) - Coordination status (
getCoordinationStatus()for debugging)
Key Constraints:
- Coordinate BGTaskScheduler with app lifecycle events
- Sync state between plugin and TimeSafari PlatformServiceMixin
- Track coordination state for debugging
Completion Criteria:
- Background tasks coordinated with app lifecycle
- Full library ready for production TimeSafari integration
- Test app ready for full coordination testing
Cross-Platform Method Equivalence Table
Canonical source of truth for all @PluginMethod implementations
Note: TypeScript signatures in this table are illustrative; the source of truth is src/definitions.ts. Any changes in definitions.ts must be reflected here and in the iOS implementation. Verify signatures match definitions.ts before implementation.
| Android Method | TypeScript Interface | iOS Swift Method | iOS File | Phase | Status |
|---|---|---|---|---|---|
configure() |
configure(options: ConfigureOptions): Promise<void> |
@objc func configure(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
1 | ✅ Partial |
scheduleDailyNotification() |
scheduleDailyNotification(options: NotificationOptions): Promise<void> |
@objc func scheduleDailyNotification(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
1 | ❌ Missing |
getLastNotification() |
getLastNotification(): Promise<NotificationResponse | null> |
@objc func getLastNotification(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
1 | ❌ Missing |
cancelAllNotifications() |
cancelAllNotifications(): Promise<void> |
@objc func cancelAllNotifications(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
1 | ❌ Missing |
getNotificationStatus() |
getNotificationStatus(): Promise<NotificationStatus> |
@objc func getNotificationStatus(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
1 | ❌ Missing |
updateSettings() |
updateSettings(settings: NotificationSettings): Promise<void> |
@objc func updateSettings(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
1 | ❌ Missing |
getBatteryStatus() |
getBatteryStatus(): Promise<BatteryStatus> |
@objc func getBatteryStatus(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
2 | ❌ Missing |
requestBatteryOptimizationExemption() |
requestBatteryOptimizationExemption(): Promise<void> |
@objc func requestBatteryOptimizationExemption(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
2 | ❌ Missing |
setAdaptiveScheduling() |
setAdaptiveScheduling(options: { enabled: boolean }): Promise<void> |
@objc func setAdaptiveScheduling(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
2 | ❌ Missing |
getPowerState() |
getPowerState(): Promise<PowerState> |
@objc func getPowerState(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
2 | ❌ Missing |
maintainRollingWindow() |
maintainRollingWindow(): Promise<void> |
@objc func maintainRollingWindow(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
2 | ❌ Missing |
getRollingWindowStats() |
getRollingWindowStats(): Promise<{stats: string, ...}> |
@objc func getRollingWindowStats(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
2 | ❌ Missing |
getExactAlarmStatus() |
getExactAlarmStatus(): Promise<{supported: boolean, ...}> |
@objc func getExactAlarmStatus(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
2 | ❌ Missing |
requestExactAlarmPermission() |
requestExactAlarmPermission(): Promise<void> |
@objc func requestExactAlarmPermission(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
2 | ❌ Missing |
openExactAlarmSettings() |
openExactAlarmSettings(): Promise<void> |
@objc func openExactAlarmSettings(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
2 | ❌ Missing |
getRebootRecoveryStatus() |
getRebootRecoveryStatus(): Promise<{inProgress: boolean, ...}> |
@objc func getRebootRecoveryStatus(_ call: CAPPluginCall) |
DailyNotificationRebootRecoveryManager.swift |
2 | ❌ Missing |
setActiveDidFromHost() |
setActiveDidFromHost(options: {activeDid: string}): Promise<void> |
@objc func setActiveDidFromHost(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
3 | ❌ Missing |
refreshAuthenticationForNewIdentity() |
refreshAuthenticationForNewIdentity(options: {activeDid: string}): Promise<void> |
@objc func refreshAuthenticationForNewIdentity(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
3 | ❌ Missing |
clearCacheForNewIdentity() |
clearCacheForNewIdentity(): Promise<void> |
@objc func clearCacheForNewIdentity(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
3 | ❌ Missing |
updateBackgroundTaskIdentity() |
updateBackgroundTaskIdentity(options: {activeDid: string}): Promise<void> |
@objc func updateBackgroundTaskIdentity(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
3 | ❌ Missing |
testJWTGeneration() |
testJWTGeneration(options?: {activeDid?: string}): Promise<{...}> |
@objc func testJWTGeneration(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
3 | ❌ Missing |
testEndorserAPI() |
testEndorserAPI(options?: {activeDid?: string, apiServer?: string}): Promise<{...}> |
@objc func testEndorserAPI(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
3 | ❌ Missing |
coordinateBackgroundTasks() |
coordinateBackgroundTasks(): Promise<void> |
@objc func coordinateBackgroundTasks(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
4 | ❌ Missing |
handleAppLifecycleEvent() |
handleAppLifecycleEvent(options: {lifecycleEvent: string}): Promise<void> |
@objc func handleAppLifecycleEvent(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
4 | ❌ Missing |
getCoordinationStatus() |
getCoordinationStatus(): Promise<{...}> |
@objc func getCoordinationStatus(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
4 | ❌ Missing |
scheduleDailyReminder() |
scheduleDailyReminder(options: ReminderOptions): Promise<void> |
@objc func scheduleDailyReminder(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
- | ✅ Complete |
cancelDailyReminder() |
cancelDailyReminder(options: {reminderId: string}): Promise<void> |
@objc func cancelDailyReminder(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
- | ✅ Complete |
getScheduledReminders() |
getScheduledReminders(): Promise<{reminders: ReminderInfo[]}> |
@objc func getScheduledReminders(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
- | ✅ Complete |
updateDailyReminder() |
updateDailyReminder(options: ReminderOptions): Promise<void> |
@objc func updateDailyReminder(_ call: CAPPluginCall) |
DailyNotificationPlugin.swift |
- | ✅ Complete |
Component Mapping: Android → iOS
Core Components
| Android Component | iOS Equivalent | Status | Notes |
|---|---|---|---|
DailyNotificationPlugin.java |
DailyNotificationPlugin.swift |
✅ Partial | Needs method additions |
DailyNotificationStorage.java |
DailyNotificationStorage.swift |
❌ Missing | Create new file |
DailyNotificationScheduler.java |
DailyNotificationScheduler.swift |
❌ Missing | Create new file |
DailyNotificationFetcher.java |
DailyNotificationBackgroundTaskManager.swift |
✅ Exists | Needs enhancement |
DailyNotificationDatabase.java |
DailyNotificationDatabase.swift |
✅ Exists | CoreData-based |
DailyNotificationRollingWindow.java |
DailyNotificationRollingWindow.swift |
✅ Exists | Needs enhancement |
DailyNotificationExactAlarmManager.java |
iOS equivalent needed | ❌ Missing | Use UNUserNotificationCenter |
DailyNotificationRebootRecoveryManager.java |
iOS equivalent needed | ❌ Missing | Use app launch detection |
DailyNotificationTTLEnforcer.java |
DailyNotificationTTLEnforcer.swift |
✅ Exists | Needs enhancement |
DailyNotificationETagManager.java |
DailyNotificationETagManager.swift |
✅ Exists | Needs enhancement |
DailyNotificationJWTManager.java |
iOS equivalent needed | ❌ Missing | Create new file |
EnhancedDailyNotificationFetcher.java |
iOS equivalent needed | ❌ Missing | Create new file |
DailyNotificationFetchWorker.java |
BGTaskScheduler handler | ❌ Missing | Implement in plugin |
DailyNotificationReceiver.java |
Notification delegate | ❌ Missing | Implement in plugin |
Platform API Mapping
| Android API | iOS Equivalent | Implementation Notes |
|---|---|---|
AlarmManager.setExact() |
UNUserNotificationCenter.add() with UNCalendarNotificationTrigger |
Use calendar-based triggers for daily scheduling |
WorkManager.enqueue() |
BGTaskScheduler.submit() |
Use BGAppRefreshTaskRequest for background fetching |
SharedPreferences |
UserDefaults |
For simple key-value storage |
SQLiteDatabase |
CoreData / SQLite.swift |
For complex data storage |
NotificationManager |
UNUserNotificationCenter |
For notification delivery |
PowerManager |
UIDevice.batteryState |
For battery status |
BroadcastReceiver |
App lifecycle notifications | For reboot recovery |
PendingIntent |
UNNotificationRequest |
For scheduled notifications |
Method Implementation Checklist
Core Methods (Phase 1)
configure(options: ConfigureOptions)- Enhanced configurationscheduleDailyNotification(options: NotificationOptions)- Main schedulinggetLastNotification()- Last notification retrievalcancelAllNotifications()- Cancel all notificationsgetNotificationStatus()- Status retrievalupdateSettings(settings: NotificationSettings)- Settings update
Power Management Methods (Phase 2)
getBatteryStatus()- Battery statusrequestBatteryOptimizationExemption()- Battery optimization (iOS: Background App Refresh)setAdaptiveScheduling(options: { enabled: boolean })- Adaptive schedulinggetPowerState()- Power state
Rolling Window Methods (Phase 2)
maintainRollingWindow()- Manual maintenancegetRollingWindowStats()- Statistics retrieval
Exact Alarm Methods (Phase 2) - iOS Equivalents
getExactAlarmStatus()- Notification delivery statusrequestExactAlarmPermission()- Notification permissionsopenExactAlarmSettings()- Open notification settings
Reboot Recovery Methods (Phase 2)
getRebootRecoveryStatus()- Recovery status
TimeSafari Integration Methods (Phase 3)
setActiveDidFromHost(options: { activeDid: string })- ActiveDid managementrefreshAuthenticationForNewIdentity(options: { activeDid: string })- Auth refreshclearCacheForNewIdentity()- Cache clearingupdateBackgroundTaskIdentity(options: { activeDid: string })- Background task identitytestJWTGeneration(options?: { activeDid?: string })- JWT testingtestEndorserAPI(options?: { activeDid?: string, apiServer?: string })- API testing
Background Coordination Methods (Phase 4)
coordinateBackgroundTasks()- Background coordinationhandleAppLifecycleEvent(options: { lifecycleEvent: string })- Lifecycle eventsgetCoordinationStatus()- Coordination status
Enhanced Dual Scheduling Methods (Already Partially Implemented)
scheduleContentFetch(config: ContentFetchConfig)- Needs enhancementscheduleUserNotification(config: UserNotificationConfig)- Needs enhancementscheduleDualNotification(config: DualScheduleConfiguration)- Needs enhancementgetDualScheduleStatus()- Needs enhancement
Reminder Methods (Already Implemented)
scheduleDailyReminder(options: ReminderOptions)- ✅ CompletecancelDailyReminder(options: { reminderId: string })- ✅ CompletegetScheduledReminders()- ✅ CompleteupdateDailyReminder(options: ReminderOptions)- ✅ Complete
Platform-Specific Considerations
Testing Environment
- Primary Testing Target: iOS Simulator
- Simulator Testing: All features should be testable on iOS Simulator
- Physical Device Testing: Optional for final validation
iOS Simulator vs Real Device Differences
| Behavior | Simulator | Real Device |
|---|---|---|
| BGTaskScheduler | Runs instantly when forced | Often delayed, may not run at all |
| Notifications | Always delivered immediately | Delivery may be delayed or batched |
| Battery state | Always "unplugged" simulation | Real battery impacts scheduling |
| App kill behavior | More predictable | iOS kills background tasks aggressively |
| Background App Refresh | Always enabled | User may disable in Settings |
| Low Power Mode | Not available | May delay/disable background tasks |
Testing Strategy:
- Develop and test primarily on iOS Simulator
- Validate critical paths on physical device before release
- Document simulator limitations in test results
iOS Notification Limits
- Maximum Pending Notifications: 64 (system limit)
- Maximum Daily Notifications: ~20 (recommended)
- Initial Scope: One prefetch + one notification per day
- Rolling Window Strategy: Always arm today's notifications, arm tomorrow's if within limits (Phase 2+)
iOS Background Execution
- BGTaskScheduler Limits:
- Background refresh tasks: ~30 seconds execution time
- Processing tasks: Variable, depends on system resources
- Strategy: Efficient processing, immediate next-schedule after completion
iOS Storage Options
- UserDefaults: For simple configuration and preferences
- CoreData: For complex relational data (schedules, content cache)
- File System: For large content (images, media)
iOS Permission Model
- Notification Permissions: Required for all notification delivery
- Background App Refresh: Required for background fetching
- Strategy: Request permissions early, provide clear explanations
Concurrency & Reentrancy Rules
Critical: All access to shared state must be serialized to prevent race conditions.
Implementation Requirements:
-
Single Actor/Queue for State Access:
- All access to
DailyNotificationDatabase,DailyNotificationStorage,DailyNotificationRollingWindow, andDailyNotificationTTLEnforcermust go through a singleactor(e.g.,DailyNotificationStateActor) or a dedicated serialDispatchQueue - No direct CoreData access from CAP bridge methods; they must delegate into the actor/queue
- Background tasks and plugin method calls must never touch the DB concurrently outside this actor/queue
- All access to
-
Actor Pattern (Recommended):
actor DailyNotificationStateActor { private let database: DailyNotificationDatabase // All DB operations here } -
Serial Queue Pattern (Alternative):
private let stateQueue = DispatchQueue(label: "com.timesafari.dailynotification.state", attributes: .serial) -
Enforcement:
- All plugin methods must call through actor/queue
- All background task handlers must call through actor/queue
- No direct property access to shared state from multiple threads
Error Code Matching with Android
Requirement: iOS must return the same error codes and JSON structure as Android.
Authoritative Source: The authoritative list of error codes is defined in Android's DailyNotificationErrorHandler (or equivalent error handling class). iOS must mirror that list exactly, including semantics.
TODO: Extract full error code table from Android implementation (src/android/DailyNotificationErrorHandler.java or equivalent) and paste here as a normative reference.
Note: This TODO is blocking for Phase 1: iOS error handling must not be considered complete until the table is extracted and mirrored. Phase 1 implementation should not proceed without verifying error code parity.
Error Response Format:
{
"error": "error_code",
"message": "Human-readable error message"
}
Common Error Codes (examples - verify against Android source):
notifications_denied- Notification permissions not grantedinvalid_time_format- Time parameter format invalidscheduling_failed- Failed to schedule notificationbackground_refresh_disabled- Background App Refresh disabledtask_scheduling_failed- BGTaskScheduler submission failed
Implementation:
- Use same error keys as Android implementation (from authoritative source)
- Use same JSON shape for all error responses
- Log using same categories as Android (
DNP-PLUGIN,DNP-SCHEDULER, etc.)
Validation & QA Plan
Unit Tests
- Test all new methods match Android behavior
- Test storage layer (UserDefaults and CoreData)
- Test scheduler with iOS notification limits
- Test background task execution
- Test TTL enforcement
- Test rolling window maintenance
Integration Tests
- Test full notification pipeline (fetch → cache → schedule → display)
- Test offline scenarios
- Test background execution reliability
- Test reboot recovery
- Test activeDid integration
Manual Testing
- Test on iOS Simulator (primary)
- Test on physical iOS devices (validation)
- Test Background App Refresh settings
- Test notification permissions
- Test battery optimization scenarios
- Test app lifecycle events
File Organization
New Files to Create
ios/Plugin/
├── DailyNotificationStorage.swift # Storage abstraction (new)
├── DailyNotificationScheduler.swift # Scheduler implementation (new)
├── DailyNotificationJWTManager.swift # JWT management (new)
├── EnhancedDailyNotificationFetcher.swift # Enhanced fetcher (new)
└── DailyNotificationRebootRecoveryManager.swift # Reboot recovery (new)
Files to Enhance
ios/Plugin/
├── DailyNotificationPlugin.swift # Add missing methods
├── DailyNotificationBackgroundTaskManager.swift # Enhance background tasks
├── DailyNotificationRollingWindow.swift # Enhance rolling window
├── DailyNotificationTTLEnforcer.swift # Enhance TTL enforcement
└── DailyNotificationETagManager.swift # Enhance ETag management
Test App Files to Create
test-apps/ios-test-app/ # New iOS test app (equivalent to android-test-app)
├── App/
│ ├── AppDelegate.swift # App delegate with plugin setup
│ ├── Info.plist # iOS permissions and config
│ ├── SceneDelegate.swift # Scene management (if needed)
│ └── Main.storyboard # Main storyboard (if needed)
├── App.xcodeproj/ # Xcode project
├── App.xcworkspace/ # Xcode workspace (if using CocoaPods)
├── Podfile # CocoaPods dependencies
└── www/ # Web assets (same as android-test-app)
├── index.html # Same UI as android-test-app
├── capacitor.config.json # Capacitor config
└── capacitor.plugins.json # Plugin registration
Build Scripts to Create
scripts/
└── build-ios-test-app.sh # iOS test app build script (new)
# Location: Root-level script (scripts/build-ios-test-app.sh)
# Pattern: Similar to scripts/build-native.sh
# Features:
# - Check environment (xcodebuild, pod)
# - cd into test-apps/ios-test-app internally
# - Install dependencies (pod install)
# - Build for simulator or device
# - Clear error messages and logging
Success Criteria
Functional Parity
- All Android
@PluginMethodmethods have iOS equivalents - All methods return same data structures as Android
- All methods handle errors consistently with Android
- All methods log consistently with Android
Platform Adaptations
- iOS uses appropriate iOS APIs (UNUserNotificationCenter, BGTaskScheduler)
- iOS respects iOS limits (64 notification limit, background execution limits)
- iOS provides iOS-specific features where appropriate (Background App Refresh)
Code Quality
- All code follows Swift best practices
- All code is documented with file-level and method-level comments
- All code includes error handling and logging
- All code is type-safe
Testing
- Unit tests cover all new methods
- Integration tests verify full pipeline
- Manual testing confirms real-world behavior
Test App
ios-test-appcreated with same UI asandroid-test-app- Test app uses iOS plugin implementation
- All permissions properly configured in Info.plist
- Build script
build-ios-test-app.shcreated and working - Build works via both Xcode GUI and command line
- Comprehensive logging accessible via Console.app
- Background task debugging tools available
Risks & Mitigations
Risk 1: iOS Background Execution Limitations
Risk: iOS background execution is more limited than Android
Mitigation:
- Use efficient processing algorithms
- Schedule next task immediately after completion
- Provide clear user guidance on Background App Refresh
Risk 2: iOS Notification Limits
Risk: iOS 64 notification limit may conflict with rolling window strategy
Mitigation:
- Implement smart rolling window that respects limits
- Prioritize today's notifications over tomorrow's
- Provide clear error messages when limits are reached
Risk 3: TypeScript Interface Compatibility
Risk: iOS implementation may not match TypeScript interface exactly
Mitigation:
- Test all methods against TypeScript interface
- Ensure return types match exactly
- Ensure error handling matches Android behavior
Risk 4: Platform-Specific Behavior Differences
Risk: iOS and Android may behave differently for same operations
Mitigation:
- Document all platform differences
- Provide platform-specific error messages where appropriate
- Test cross-platform compatibility
Timeline Estimate
Phase 1: Core Infrastructure
- Storage layer implementation
- Scheduler implementation (single daily schedule: one prefetch + one notification)
- Prefetch scheduled 5 minutes before notification time
- Background fetching enhancement (single daily prefetch, 5 minutes before notification)
- Core methods implementation
- iOS test app creation (ios-test-app)
- Build script creation (build-ios-test-app.sh)
- Testing on iOS Simulator
Test App Readiness: ✅ Ready for testing after Phase 1 completion
- Basic scheduling (one prefetch 5 minutes before + one notification per day)
- Core plugin methods functional
- Testable on iOS Simulator
Phase 2: Advanced Features
- Rolling window enhancement (expands beyond single daily schedule)
- TTL enforcement enhancement
- Exact alarm equivalent implementation
- Reboot recovery implementation
- Power management enhancement
- Testing on iOS Simulator
Test App Readiness: ✅ Ready for testing after Phase 2 completion
- Rolling window and advanced features functional
- Testable on iOS Simulator
Phase 3: TimeSafari Integration
- JWT management implementation
- ETag management enhancement
- Enhanced fetcher implementation
- Phase 1 methods implementation
- Testing on iOS Simulator
TimeSafari Integration Readiness: ✅ Library ready for TimeSafari integration after Phase 3 completion Test App Readiness: ✅ Ready for testing after Phase 3 completion
- TimeSafari integration features functional
- Testable on iOS Simulator
Phase 4: Background Coordination
- Background coordination implementation
- Lifecycle management implementation
- Coordination status implementation
- Testing on iOS Simulator
TimeSafari Integration Readiness: ✅ Full library ready for production TimeSafari integration after Phase 4 completion Test App Readiness: ✅ Ready for testing after Phase 4 completion
- Full background coordination functional
- Testable on iOS Simulator
Dependencies
External Dependencies
- Capacitor: iOS plugin framework
- UserNotifications: iOS notification framework
- BackgroundTasks: iOS background execution framework
- CoreData: iOS data persistence framework
Internal Dependencies
- TypeScript Interface: Must remain unchanged (
src/definitions.ts) - Android Implementation: Reference implementation (read-only)
- Existing iOS Components: Build upon existing Swift files
References
Android Implementation Files
src/android/DailyNotificationPlugin.java- Main plugin classsrc/android/DailyNotificationStorage.java- Storage layersrc/android/DailyNotificationScheduler.java- Schedulersrc/android/DailyNotificationFetcher.java- Background fetchingsrc/android/DailyNotificationDatabase.java- Database managementsrc/android/DailyNotificationRollingWindow.java- Rolling windowsrc/android/DailyNotificationExactAlarmManager.java- Exact alarmssrc/android/DailyNotificationRebootRecoveryManager.java- Reboot recoverysrc/android/DailyNotificationTTLEnforcer.java- TTL enforcementsrc/android/DailyNotificationETagManager.java- ETag managementsrc/android/DailyNotificationJWTManager.java- JWT managementsrc/android/EnhancedDailyNotificationFetcher.java- Enhanced fetcher
iOS Implementation Files
ios/Plugin/DailyNotificationPlugin.swift- Main plugin classios/Plugin/DailyNotificationDatabase.swift- Database (CoreData)ios/Plugin/DailyNotificationRollingWindow.swift- Rolling windowios/Plugin/DailyNotificationTTLEnforcer.swift- TTL enforcementios/Plugin/DailyNotificationETagManager.swift- ETag managementios/Plugin/DailyNotificationBackgroundTaskManager.swift- Background tasks
TypeScript Interface
src/definitions.ts- Complete TypeScript interface definition
Documentation
doc/implementation-roadmap.md- Implementation roadmapdoc/INTEGRATION_CHECKLIST.md- Integration checklistdoc/test-app-ios/IOS_TEST_APP_REQUIREMENTS.md- iOS test app detailed requirements (must be created in Phase 1)README.md- Project documentation
Competence Hooks
Why This Works
- Platform Parity: Matching Android functionality ensures consistent user experience across platforms
- Incremental Approach: Phased implementation reduces risk and allows for iterative testing
- Platform Adaptations: Using iOS-native APIs ensures optimal performance and reliability
Common Pitfalls
- Background Execution Limits: iOS background execution is more limited than Android - must design for efficiency
- Notification Limits: iOS 64 notification limit requires careful rolling window management
- Permission Model: iOS requires explicit permission requests - must handle gracefully
Next Skill Unlock
- iOS Background Execution: Understanding BGTaskScheduler and background execution patterns
- iOS Notification System: Deep dive into UNUserNotificationCenter and notification delivery guarantees
Teach-Back
Question: "How does iOS background execution differ from Android, and how does this affect the notification scheduling strategy?"
Expected Answer: iOS background execution is more limited (30 seconds for refresh tasks) and less reliable than Android WorkManager. This requires efficient processing, immediate next-schedule after completion, and fallback strategies for when background execution fails.
Collaboration Hooks
Discussion Points
- Platform Differences: How should we handle platform-specific behavior differences in error messages and user guidance?
- Testing Strategy: What level of cross-platform testing is needed to ensure parity?
- Performance: How do we ensure iOS implementation performs as well as Android?
Review Points
- Code Review: All new iOS code should be reviewed against Android implementation for functional parity
- Architecture Review: iOS component architecture should be reviewed for maintainability
- Testing Review: Test coverage should be reviewed for completeness
Stakeholders
- iOS Developer: Primary implementer
- Android Developer: Reference implementation owner (consultation)
- TypeScript Developer: Interface maintainer (consultation)
- QA: Testing and validation
Decision Log
2025-11-13: Directive Created
Decision: Create comprehensive directive for iOS Android parity implementation
Rationale: Need clear plan for upgrading iOS while preserving Android and TypeScript interface
Status: ✅ Approved for implementation
Validation Matrix
Cross-platform feature validation checklist
| Feature | Android Behavior | iOS Equivalent | Verified By | Phase |
|---|---|---|---|---|
| Daily notification | Exact ±0s | ±180s tolerance¹ | Test case: verify observed delivery within 3 min window | 1 |
| Daily prefetch | Worker at exact time | BGTask within 15 min window | Logs + UI: check fetch timestamp | 1 |
| Rolling window | 2 days ahead | iOS 64 limit enforced | Stats call: verify pending count ≤ 64 | 2 |
| TTL enforcement | Discard stale at fire | Same: discard stale at fire | Deliver-time check: verify TTL validation | 2 |
| Reboot recovery | BOOT_COMPLETED | Uptime comparison | App launch: verify reschedule after reboot | 2 |
| JWT authentication | ES256K signing | Same algorithm | Test method: verify token generation | 3 |
| ETag caching | Conditional requests | Same: If-None-Match headers | Network logs: verify 304 responses | 3 |
| Background coordination | WorkManager sync | BGTaskScheduler sync | Lifecycle events: verify coordination | 4 |
¹ Note: If drift exceeds 180s, log [DNP-SCHEDULER] notification drift > 180s and capture in diagnostics; retry logic is not required for v1.
Glossary
ETag: HTTP entity tag used for conditional requests. Allows server to return 304 Not Modified if content unchanged.
Rolling Window: Strategy of scheduling notifications for today and tomorrow to ensure delivery even if system delays occur.
Prefetch: Background task that fetches notification content before the notification is scheduled to fire.
ActiveDID: Decentralized identifier (DID) representing the currently active user identity in TimeSafari.
Exact Alarm: Android feature allowing precise scheduling at exact time. iOS equivalent uses calendar triggers with tolerance.
TTL (Time To Live): Maximum age of cached content. Content older than TTL is considered stale and discarded.
BGTaskScheduler: iOS framework for scheduling background tasks (fetch, processing) that run when system conditions allow.
UNUserNotificationCenter: iOS framework for scheduling and delivering local notifications to users.
Developer Onboarding
Required Environment
- Xcode Version: 15.0 or later (for iOS 17+ features)
- macOS Version: 13.0 (Ventura) or later
- Swift Version: 5.9 or later
- iOS Deployment Target: iOS 15.0 or later
Running the Plugin from Test App
-
Build Test App:
# From repo root ./scripts/build-ios-test-app.sh --simulatorNote: The build script handles
cdintotest-apps/ios-test-appinternally. -
Open in Xcode:
cd test-apps/ios-test-app open App.xcworkspace # or App.xcodeproj -
Run on Simulator:
- Select target device (iPhone 15, etc.)
- Press Cmd+R to build and run
Common Failure Modes
-
BGTaskScheduler Not Running:
- Check Info.plist has
BGTaskSchedulerPermittedIdentifiers - Verify task registered in AppDelegate before app finishes launching
- Simulator-only debugging trick: Use LLDB command to manually trigger:
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.timesafari.dailynotification.fetch"]- Note: This is for simulator testing only, not available in production
- Check Info.plist has
-
Notifications Not Delivering:
- Check notification permissions:
UNUserNotificationCenter.current().getNotificationSettings() - Verify notification scheduled:
UNUserNotificationCenter.current().getPendingNotificationRequests() - Check notification category registered
- Check notification permissions:
-
Build Failures:
- Ensure CocoaPods installed:
pod install - Check Capacitor plugin path in
capacitor.plugins.json - Verify Xcode scheme matches workspace
- Ensure CocoaPods installed:
-
Background Tasks Expiring:
- BGTaskScheduler tasks have ~30 second execution window
- Must call
task.setTaskCompleted(success:)before expiration - Schedule next task immediately after completion
iOS Test App Requirements
Note: Detailed test app requirements have been moved to a separate document for clarity.
Reference: See doc/test-app-ios/IOS_TEST_APP_REQUIREMENTS.md for:
- UI parity requirements
- iOS permissions configuration
- Build options (Xcode GUI and command-line)
- Debugging strategy
- Test app implementation checklist
Important: If doc/test-app-ios/IOS_TEST_APP_REQUIREMENTS.md does not yet exist, it MUST be created as part of Phase 1 before implementation starts.
Quick Reference:
- Test app location:
test-apps/ios-test-app/ - Build script:
scripts/build-ios-test-app.sh(root-level) - Same UI as
android-test-app(HTML/JS)
Status: 🎯 READY FOR IMPLEMENTATION
Next Steps: Begin Phase 1 implementation after directive approval