# 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 1–3 directives below. **If any code or behavior in this file conflicts with a Phase directive, the Phase directive wins.** **Reference**: See [Plugin Requirements](./alarms/03-plugin-requirements.md) for requirements that Phase directives implement. **Reference**: See [Platform Capability Reference](./alarms/01-platform-capability-reference.md) for iOS OS-level facts. **⚠️ IMPORTANT**: For implementation, use the phase-specific directives (these are the canonical source of truth): - **[Phase 1: Cold Start Recovery](./ios-implementation-directive-phase1.md)** - Minimal viable recovery - Implements: [Plugin Requirements §3.1.2](./alarms/03-plugin-requirements.md#312-app-cold-start) (iOS equivalent) - Explicit acceptance criteria, rollback safety, data integrity checks - **Start here** for fastest implementation - **[Phase 2: App Termination Detection & Recovery](./ios-implementation-directive-phase2.md)** - Comprehensive termination handling - Implements: iOS-specific app termination scenarios - Prerequisite: Phase 1 complete - **[Phase 3: Background Task Registration & Boot Recovery](./ios-implementation-directive-phase3.md)** - Background task enhancement - Implements: BGTaskScheduler registration for boot recovery - Prerequisites: Phase 1 and Phase 2 complete **See Also**: [Unified Alarm Directive](./alarms/000-UNIFIED-ALARM-DIRECTIVE.md) 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](./ios-implementation-directive-phase1.md) 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](./ios-implementation-directive-phase2.md) for implementation** **Phase 3** – Background task registration & boot recovery - BGTaskScheduler registration for boot recovery - Next occurrence rescheduled for repeating schedules - **See [Phase 3 directive](./ios-implementation-directive-phase3.md) 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](./alarms/01-platform-capability-reference.md#3-ios-notification-capability-matrix) 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**: - [Phase 1: ReactivationManager creation](./ios-implementation-directive-phase1.md#2-implementation-reactivationmanager) - [Phase 2: Termination detection](./ios-implementation-directive-phase2.md#2-implementation-termination-detection) ### 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. ```swift 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](./alarms/01-platform-capability-reference.md#311-notifications-survive-app-termination) - Notifications survive app termination **Detection Logic**: ```swift 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](./ios-implementation-directive-phase1.md#3-scenario-detection) --- ## 5. Missed Notification Detection ### 5.1 Detection Logic **Platform Reference**: [iOS §3.2.1](./alarms/01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) - 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](./ios-implementation-directive-phase1.md#4-missed-notification-detection) --- ## 6. Background Task Registration ### 6.1 BGTaskScheduler Registration **Platform Reference**: [iOS §3.1.3](./alarms/01-platform-capability-reference.md#313-background-tasks-for-prefetching) - 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](./ios-implementation-directive-phase3.md#2-background-task-registration) --- ## 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](../test-apps/ios-test-app/test-phase1.sh) --- ## 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](./ios-implementation-directive-phase1.md) - Focus on missed notification detection - Verify future notifications are scheduled 2. **Proceed to Phase 2**: Add termination detection - See [Phase 2 directive](./ios-implementation-directive-phase2.md) - Implement comprehensive recovery - Handle multiple schedules 3. **Complete Phase 3**: Background task registration - See [Phase 3 directive](./ios-implementation-directive-phase3.md) - Register BGTaskScheduler handlers - Implement boot recovery --- ## 10. References - [Platform Capability Reference](./alarms/01-platform-capability-reference.md) - iOS OS-level facts - [Plugin Requirements](./alarms/03-plugin-requirements.md) - Requirements this directive implements - [Android Implementation Directive](./android-implementation-directive.md) - Android equivalent for comparison - [iOS Recovery Scenario Mapping](./ios-recovery-scenario-mapping.md) - Detailed scenario mapping - [iOS Core Data Migration Guide](./ios-core-data-migration.md) - Database migration guide --- **Document Version**: 1.0.0 **Last Updated**: 2025-12-08 **Next Review**: After Phase 1 implementation