# 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

