Files
daily-notification-plugin/doc/platform/ios/IMPLEMENTATION_DIRECTIVE.md

14 KiB
Raw Blame History

iOS Implementation Directive: App Launch Recovery & Missed Notification Detection

Author: Matthew Raymer
Date: 2025-12-08
Status: Active Implementation Directive - iOS Only
Version: 1.0.0
Last Synced With Plugin Version: v1.1.0

Purpose

This directive provides descriptive overview and integration guidance for iOS-specific recovery and missed notification detection:

  1. App Launch Recovery (cold/warm/terminated)
  2. Missed Notification Detection
  3. App Termination Detection
  4. Background Task Registration for Boot Recovery

⚠️ CRITICAL: This document is descriptive and integrative. The normative implementation instructions are in the Phase 13 directives below. If any code or behavior in this file conflicts with a Phase directive, the Phase directive wins.

Reference: See Plugin Requirements for requirements that Phase directives implement.

Reference: See Platform Capability Reference for iOS OS-level facts.

⚠️ IMPORTANT: For implementation, use the phase-specific directives (these are the canonical source of truth):

See Also: Unified Alarm Directive for master coordination document.


1. Implementation Overview

1.1 What Needs to Be Implemented

Feature Status Priority Location
App Launch Recovery Missing High DailyNotificationPlugin.swift - load() method
Missed Notification Detection ⚠️ Partial High DailyNotificationPlugin.swift - new method
App Termination Detection Missing High DailyNotificationPlugin.swift - recovery logic
Background Task Registration ⚠️ Partial Medium AppDelegate.swift - BGTaskScheduler registration

1.2 Implementation Strategy

Phase 1 Cold start recovery only

  • Missed notification detection + future notification verification
  • No termination detection, no boot handling
  • See Phase 1 directive for implementation

Phase 2 App termination detection & full recovery

  • Termination detection via UNUserNotificationCenter state comparison
  • Comprehensive recovery of all schedules (notify + fetch)
  • Past notifications marked as missed, future notifications rescheduled
  • See Phase 2 directive for implementation

Phase 3 Background task registration & boot recovery

  • BGTaskScheduler registration for boot recovery
  • Next occurrence rescheduled for repeating schedules
  • See Phase 3 directive for implementation

2. iOS-Specific Considerations

2.1 Key Differences from Android

iOS Advantages:

  • Notifications persist across app termination (OS-guaranteed)
  • Notifications persist across device reboot (OS-guaranteed)
  • No force stop equivalent (iOS doesn't have user-facing force stop)

iOS Challenges:

  • App code does NOT run when notification fires (only if user taps)
  • Background execution severely limited (BGTaskScheduler only)
  • Cannot rely on background execution for recovery
  • Must detect missed notifications on app launch

Platform Reference: See Platform Capability Reference §3 for complete iOS behavior matrix.

2.2 Recovery Scenario Mapping

Android → iOS Mapping:

Android Scenario iOS Equivalent Detection Method
COLD_START App Launch After Termination Check if notifications exist vs DB state
FORCE_STOP App Terminated by System Check if notifications missing vs DB state
BOOT Device Reboot BGTaskScheduler registration (Phase 3)
WARM_START App Resume (Foreground) Check app state on resume

Note: iOS doesn't have a user-facing "force stop" equivalent. System termination is detected by comparing UNUserNotificationCenter state with database state.

2.3 iOS APIs Used

Notification Management:

  • UNUserNotificationCenter.current() - Notification center
  • UNUserNotificationCenter.getPendingNotificationRequests() - Check scheduled notifications
  • UNUserNotificationCenter.add() - Schedule notifications

Background Tasks:

  • BGTaskScheduler.shared - Background task scheduler
  • BGTaskScheduler.register() - Register background task handlers
  • BGAppRefreshTaskRequest - Background fetch requests

App Lifecycle:

  • applicationWillTerminate - App termination notification
  • applicationDidBecomeActive - App foreground notification
  • applicationDidEnterBackground - App background notification

3. Implementation: ReactivationManager (iOS)

⚠️ Illustrative only See Phase 1 and Phase 2 directives for canonical implementation.

ReactivationManager Responsibilities by Phase:

Phase Responsibilities
1 Cold start only (missed detection + verify/reschedule future)
2 Adds termination detection & recovery
3 Background task registration & boot recovery

For implementation details, see:

3.1 Create New File

File: ios/Plugin/DailyNotificationReactivationManager.swift

Purpose: Centralized recovery logic for app launch scenarios

3.2 Class Structure

⚠️ Illustrative only See Phase 1 for canonical implementation.

import Foundation
import UserNotifications

/**
 * Manages recovery of notifications on app launch
 * Handles cold start, warm start, and termination recovery scenarios
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 */
class DailyNotificationReactivationManager {
    
    private static let TAG = "DNP-REACTIVATION"
    private let notificationCenter = UNUserNotificationCenter.current()
    private let database: DailyNotificationDatabase
    private let storage: DailyNotificationStorage
    
    init(database: DailyNotificationDatabase, storage: DailyNotificationStorage) {
        self.database = database
        self.storage = storage
    }
    
    /**
     * Perform recovery on app launch
     * Detects scenario (cold/warm/termination) and handles accordingly
     */
    func performRecovery() async {
        do {
            NSLog("\(Self.TAG): Starting app launch recovery")
            
            // Step 1: Detect scenario
            let scenario = try await detectScenario()
            NSLog("\(Self.TAG): Detected scenario: \(scenario)")
            
            // Step 2: Handle based on scenario
            switch scenario {
            case .termination:
                try await handleTerminationRecovery()
            case .coldStart:
                try await handleColdStartRecovery()
            case .warmStart:
                try await handleWarmStartRecovery()
            case .none:
                NSLog("\(Self.TAG): No recovery needed")
            }
            
            NSLog("\(Self.TAG): App launch recovery completed")
        } catch {
            NSLog("\(Self.TAG): Error during app launch recovery: \(error)")
        }
    }
    
    // ... implementation methods below ...
}

4. Recovery Scenario Detection

4.1 Scenario Detection Algorithm

Platform Reference: iOS §3.1.1 - Notifications survive app termination

Detection Logic:

enum RecoveryScenario {
    case none          // No recovery needed (first launch or warm resume)
    case coldStart     // App launched after termination, notifications may exist
    case termination   // App terminated, notifications missing vs DB
    case warmStart     // App resumed from background (optimization only)
}

func detectScenario() async throws -> RecoveryScenario {
    // Step 1: Check if database has schedules
    let schedules = try database.getEnabledSchedules()
    if schedules.isEmpty {
        return .none  // First launch
    }
    
    // Step 2: Get pending notifications from UNUserNotificationCenter
    let pendingNotifications = try await notificationCenter.pendingNotificationRequests()
    
    // Step 3: Compare DB state with notification center state
    let dbNotificationIds = Set(schedules.flatMap { $0.getScheduledNotificationIds() })
    let pendingIds = Set(pendingNotifications.map { $0.identifier })
    
    // Step 4: Determine scenario
    if pendingIds.isEmpty && !dbNotificationIds.isEmpty {
        // DB has schedules but no notifications scheduled
        return .termination
    } else if !pendingIds.isEmpty && !dbNotificationIds.isEmpty {
        // Both have data - check if they match
        if dbNotificationIds != pendingIds {
            return .coldStart  // Mismatch indicates recovery needed
        } else {
            return .warmStart  // Match indicates warm resume
        }
    }
    
    return .none
}

For complete implementation, see: Phase 1 directive


5. Missed Notification Detection

5.1 Detection Logic

Platform Reference: iOS §3.2.1 - App code does not run when notification fires

iOS Behavior: When a notification fires, the app code does NOT execute. The notification is displayed, but the app must detect missed notifications on the next app launch.

Detection Steps:

  1. Query database for notifications with scheduled_time < currentTime
  2. Filter for notifications with delivery_status != 'delivered'
  3. Mark as 'missed' in database
  4. Record in history table

For complete implementation, see: Phase 1 directive


6. Background Task Registration

6.1 BGTaskScheduler Registration

Platform Reference: iOS §3.1.3 - Background tasks for prefetching

iOS Limitation: BGTaskScheduler cannot be used for critical scheduling. It's system-controlled and not guaranteed.

Use Case: BGTaskScheduler is used for:

  • Prefetching content (not critical timing)
  • Boot recovery (system may defer)
  • Background maintenance (best effort)

Registration Location: AppDelegate.swift or SceneDelegate.swift

For complete implementation, see: Phase 3 directive


7. Testing Strategy

7.1 iOS Testing Tools

Simulator Testing:

  • xcrun simctl - Simulator control
  • Xcode Instruments - Performance profiling
  • Console.app - System log viewing

Device Testing:

  • Xcode Device Console - Real device logs
  • Settings → Developer → Background App Refresh - Control background execution

7.2 Test Scenarios

Phase 1 Tests:

  • Cold start recovery
  • Missed notification detection
  • Future notification verification

Phase 2 Tests:

  • App termination detection
  • Comprehensive recovery
  • Multiple schedules recovery

Phase 3 Tests:

  • Background task registration
  • Boot recovery (simulated)
  • Background task execution

For complete test procedures, see: iOS Test Scripts


8. Platform-Specific Notes

8.1 Notification Persistence

iOS Advantage: Notifications persist automatically across:

  • App termination
  • Device reboot (for calendar/time triggers)

App Responsibility: Must still:

  • Detect missed notifications on app launch
  • Reschedule future notifications if needed
  • Track delivery status in database

8.2 Background Execution Limits

iOS Limitation: Background execution is severely limited:

  • BGTaskScheduler is system-controlled
  • Cannot rely on background execution for recovery
  • Must handle recovery on app launch

Workaround: Use BGTaskScheduler for prefetching only, not for critical scheduling.

8.3 Timing Tolerance

iOS Limitation: Calendar-based notifications have ±180 second tolerance.

Impact: Notifications may fire up to 3 minutes early or late.

Mitigation: Account for tolerance in missed notification detection logic.


9. Next Steps

  1. Start with Phase 1: Implement cold start recovery

    • See Phase 1 directive
    • Focus on missed notification detection
    • Verify future notifications are scheduled
  2. Proceed to Phase 2: Add termination detection

  3. Complete Phase 3: Background task registration


10. References


Document Version: 1.0.0
Last Updated: 2025-12-08
Next Review: After Phase 1 implementation