# iOS Recovery Scenario Mapping: Android โ†’ iOS Equivalents **Author**: Matthew Raymer **Date**: 2025-12-08 **Status**: ๐ŸŽฏ **ACTIVE** - Recovery Scenario Mapping Reference **Version**: 1.0.0 **Last Synced With Plugin Version**: v1.1.0 ## Purpose This document maps Android recovery scenarios to their iOS equivalents, providing a clear translation guide for implementing iOS recovery logic based on Android patterns. **Reference**: - [Android Implementation Directive](./android-implementation-directive.md) - Android scenarios - [iOS Implementation Directive](./ios-implementation-directive.md) - iOS scenarios - [Platform Capability Reference](./alarms/01-platform-capability-reference.md) - OS-level facts --- ## 1. Scenario Mapping Overview ### 1.1 Direct Mappings | Android Scenario | iOS Equivalent | Detection Method | Recovery Action | | ---------------- | -------------- | ---------------- | --------------- | | `COLD_START` | App Launch After Termination | Compare UNUserNotificationCenter vs DB | Detect missed, verify future | | `FORCE_STOP` | App Terminated by System | DB has schedules, no notifications | Full recovery of all schedules | | `BOOT` | Device Reboot | BGTaskScheduler registration | Reschedule all notifications | | `WARM_START` | App Resume (Foreground) | Notifications match DB state | No recovery needed (optimization) | | `NONE` | First Launch / No Recovery | Empty database | No action needed | ### 1.2 Key Differences **iOS Advantages**: - โœ… Notifications persist across termination (OS-guaranteed) - โœ… Notifications persist across reboot (OS-guaranteed) - โŒ No user-facing "force stop" equivalent **iOS Challenges**: - โŒ App code does NOT run when notification fires - โŒ Must detect missed notifications on app launch - โŒ Background execution severely limited --- ## 2. Detailed Scenario Mappings ### 2.1 COLD_START โ†’ App Launch After Termination **Android Definition**: - Process killed, alarms may or may not exist - Database still populated - Alarms may have been cleared by OS **iOS Equivalent**: - App terminated by system or user - Notifications may still exist (OS-guaranteed persistence) - Database still populated - Need to verify notification state matches database **Detection Logic**: **Android**: ```kotlin // Check if alarms exist in AlarmManager val alarmsExist = alarmManager.hasAlarm(pendingIntent) if (alarmsExist && dbHasSchedules) { return COLD_START } ``` **iOS**: ```swift // Check if notifications exist in UNUserNotificationCenter let pendingNotifications = try await notificationCenter.pendingNotificationRequests() let dbSchedules = try database.getEnabledSchedules() if !pendingNotifications.isEmpty && !dbSchedules.isEmpty { // Compare notification IDs with DB state let dbIds = Set(dbSchedules.flatMap { $0.getScheduledNotificationIds() }) let pendingIds = Set(pendingNotifications.map { $0.identifier }) if dbIds != pendingIds { return .coldStart // Mismatch indicates recovery needed } } ``` **Recovery Actions**: 1. Detect missed notifications (scheduled_time < now, not delivered) 2. Mark missed notifications in database 3. Verify future notifications are scheduled 4. Reschedule missing future notifications **Platform Reference**: [iOS ยง3.1.1](./alarms/01-platform-capability-reference.md#311-notifications-survive-app-termination) --- ### 2.2 FORCE_STOP โ†’ App Terminated by System **Android Definition**: - User force-stopped app via Settings - All alarms cleared - Database still populated - Boot receiver blocked until user launches app **iOS Equivalent**: - App terminated by system (low memory, etc.) - Notifications may be missing (system cleared them) - Database still populated - No user-facing force stop equivalent **Key Difference**: iOS doesn't have a user-facing "force stop" option. System termination is the closest equivalent. **Detection Logic**: **Android**: ```kotlin // Check if alarms exist val alarmsExist = alarmManager.hasAlarm(pendingIntent) if (!alarmsExist && dbHasSchedules && !isBootRecent) { return FORCE_STOP } ``` **iOS**: ```swift // Check if notifications exist let pendingNotifications = try await notificationCenter.pendingNotificationRequests() let dbSchedules = try database.getEnabledSchedules() if pendingNotifications.isEmpty && !dbSchedules.isEmpty { // DB has schedules but no notifications scheduled return .termination } ``` **Recovery Actions**: 1. Detect all missed notifications 2. Mark all missed notifications in database 3. Reschedule all future notifications 4. Reschedule all fetch schedules (if applicable) **Platform Reference**: [iOS ยง3.2.1](./alarms/01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) --- ### 2.3 BOOT โ†’ Device Reboot **Android Definition**: - Device rebooted - All alarms wiped (OS behavior) - Database still populated - Boot receiver executes after boot completes **iOS Equivalent**: - Device rebooted - Notifications persist automatically (OS-guaranteed) - Database still populated - BGTaskScheduler may execute (system-controlled) **Key Difference**: iOS automatically persists notifications across reboot. Android requires manual rescheduling. **Detection Logic**: **Android**: ```kotlin // Check boot flag (set by BootReceiver) val bootFlag = sharedPreferences.getLong("last_boot_time", 0) val currentTime = System.currentTimeMillis() if (bootFlag > 0 && (currentTime - bootFlag) < 60000) { return BOOT } ``` **iOS**: ```swift // BGTaskScheduler registration handles boot // Check if this is a boot-triggered background task if isBootBackgroundTask { return .boot } // Or detect on app launch after reboot let lastLaunchTime = UserDefaults.standard.double(forKey: "last_launch_time") let bootTime = ProcessInfo.processInfo.systemUptime if lastLaunchTime > 0 && bootTime < 60 { return .boot } ``` **Recovery Actions**: 1. Verify notifications still exist (iOS usually handles this) 2. Detect any missed notifications during reboot window 3. Reschedule any missing notifications 4. Update next run times for repeating schedules **Platform Reference**: [iOS ยง3.1.2](./alarms/01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) --- ### 2.4 WARM_START โ†’ App Resume (Foreground) **Android Definition**: - App resumed from background - Alarms still exist - Database matches alarm state - No recovery needed (optimization) **iOS Equivalent**: - App resumed from background - Notifications still exist - Database matches notification state - No recovery needed (optimization) **Detection Logic**: **Android**: ```kotlin // Check if alarms exist and match DB val alarmsExist = alarmManager.hasAlarm(pendingIntent) if (alarmsExist && dbMatchesAlarms) { return WARM_START } ``` **iOS**: ```swift // Check if notifications exist and match DB let pendingNotifications = try await notificationCenter.pendingNotificationRequests() let dbSchedules = try database.getEnabledSchedules() let dbIds = Set(dbSchedules.flatMap { $0.getScheduledNotificationIds() }) let pendingIds = Set(pendingNotifications.map { $0.identifier }) if dbIds == pendingIds { return .warmStart // Match indicates warm resume } ``` **Recovery Actions**: - None (optimization only) - May perform lightweight verification - May update metrics --- ### 2.5 NONE โ†’ First Launch / No Recovery **Android Definition**: - First app launch - Empty database - No schedules configured - No recovery needed **iOS Equivalent**: - First app launch - Empty database - No schedules configured - No recovery needed **Detection Logic**: **Android**: ```kotlin // Check if database is empty val schedules = database.scheduleDao().getEnabled() if (schedules.isEmpty()) { return NONE } ``` **iOS**: ```swift // Check if database is empty let schedules = try database.getEnabledSchedules() if schedules.isEmpty { return .none } ``` **Recovery Actions**: - None --- ## 3. Recovery Action Mapping ### 3.1 Missed Notification Detection **Android**: - Query AlarmManager for past alarms - Check database for undelivered notifications - Mark as missed in database **iOS**: - Query database for past scheduled notifications - Check delivery status - Mark as missed in database **Key Difference**: iOS cannot query past notifications from UNUserNotificationCenter. Must rely on database state. ### 3.2 Future Notification Verification **Android**: - Query AlarmManager for future alarms - Compare with database schedules - Reschedule missing alarms **iOS**: - Query UNUserNotificationCenter for pending notifications - Compare with database schedules - Reschedule missing notifications **Key Difference**: iOS uses UNUserNotificationCenter instead of AlarmManager. ### 3.3 Full Recovery **Android**: - Reschedule all notify schedules - Reschedule all fetch schedules (WorkManager) - Mark past notifications as missed **iOS**: - Reschedule all notify schedules - Reschedule all fetch schedules (BGTaskScheduler) - Mark past notifications as missed **Key Difference**: iOS uses BGTaskScheduler instead of WorkManager. --- ## 4. Implementation Checklist ### 4.1 Phase 1: Cold Start Recovery - [ ] Implement scenario detection (cold start) - [ ] Implement missed notification detection - [ ] Implement future notification verification - [ ] Test cold start recovery ### 4.2 Phase 2: Termination Detection - [ ] Implement termination detection - [ ] Implement full recovery logic - [ ] Test termination recovery ### 4.3 Phase 3: Boot Recovery - [ ] Implement BGTaskScheduler registration - [ ] Implement boot detection - [ ] Test boot recovery --- ## 5. Platform-Specific Notes ### 5.1 iOS Advantages 1. **Notification Persistence**: iOS automatically persists notifications across termination and reboot 2. **No Force Stop**: iOS doesn't have user-facing force stop, reducing complexity 3. **Simplified Recovery**: Less recovery needed due to OS persistence ### 5.2 iOS Challenges 1. **No Code Execution on Fire**: App code doesn't run when notification fires 2. **Background Limits**: Severely limited background execution 3. **Timing Tolerance**: ยฑ180 second tolerance for calendar triggers ### 5.3 Android Advantages 1. **Code Execution on Fire**: PendingIntent can execute code when alarm fires 2. **WorkManager**: More reliable background execution 3. **Exact Timing**: Can achieve exact timing with permission ### 5.4 Android Challenges 1. **No Persistence**: Alarms don't persist across reboot 2. **Force Stop**: Hard kill that cannot be bypassed 3. **Boot Recovery**: Must implement boot receiver --- ## 6. Testing Strategy ### 6.1 Scenario Testing **Cold Start**: 1. Terminate app (swipe away) 2. Wait for notification time to pass 3. Launch app 4. Verify missed notification detection 5. Verify future notifications rescheduled **Termination**: 1. Schedule notifications 2. Terminate app 3. Clear notifications (simulate system clearing) 4. Launch app 5. Verify full recovery **Boot**: 1. Schedule notifications 2. Reboot device (or simulate) 3. Launch app 4. Verify notifications still exist 5. Verify any missed notifications detected --- ## 7. References - [Android Implementation Directive](./android-implementation-directive.md) - Android scenarios - [iOS Implementation Directive](./ios-implementation-directive.md) - iOS scenarios - [Platform Capability Reference](./alarms/01-platform-capability-reference.md) - OS-level facts - [Plugin Requirements](./alarms/03-plugin-requirements.md) - Requirements --- **Document Version**: 1.0.0 **Last Updated**: 2025-12-08 **Next Review**: After Phase 1 implementation