Consolidate all markdown documentation into organized structure per CONSOLIDATION_DIRECTIVE. All files preserved (canonical, merged, or archived). - docs/integration/ - Integration documentation (7 files) - docs/platform/ios/ - iOS platform docs (12 files) - docs/platform/android/ - Android platform docs (9 files) - docs/testing/ - Testing documentation (15 files) - docs/design/ - Design & research (5 files) - docs/ai/ - AI/ChatGPT artifacts (7 files) - docs/archive/2025-legacy-doc/ - Historical docs (17 files) - Integration: Root INTEGRATION_GUIDE.md → docs/integration/ - Platform: Separated iOS and Android into platform/ subdirectories - Testing: Consolidated all testing docs to docs/testing/ - Legacy: Archived entire doc/ directory to archive/ - AI: Moved all ChatGPT artifacts to docs/ai/ - Added docs/00-INDEX.md - Central navigation hub - Added docs/CONSOLIDATION_SOURCE_MAP.md - Complete audit trail - Added docs/CONSOLIDATION_COMPLETE.md - Consolidation summary - Updated README.md with links to documentation index - All 139 files have destinations (see CONSOLIDATION_SOURCE_MAP.md) - Zero information loss (all files preserved) - Archive preserves original structure - Index provides clear navigation - 87 files moved/created/updated - Root-level docs consolidated - Legacy doc/ directory archived - Test app docs remain with test apps (indexed) Ref: CONSOLIDATION_DIRECTIVE Author: Matthew Raymer
11 KiB
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 scenarios
- iOS Implementation Directive - iOS scenarios
- Platform Capability Reference - 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:
// Check if alarms exist in AlarmManager
val alarmsExist = alarmManager.hasAlarm(pendingIntent)
if (alarmsExist && dbHasSchedules) {
return COLD_START
}
iOS:
// 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:
- Detect missed notifications (scheduled_time < now, not delivered)
- Mark missed notifications in database
- Verify future notifications are scheduled
- Reschedule missing future notifications
Platform Reference: iOS §3.1.1
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:
// Check if alarms exist
val alarmsExist = alarmManager.hasAlarm(pendingIntent)
if (!alarmsExist && dbHasSchedules && !isBootRecent) {
return FORCE_STOP
}
iOS:
// 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:
- Detect all missed notifications
- Mark all missed notifications in database
- Reschedule all future notifications
- Reschedule all fetch schedules (if applicable)
Platform Reference: iOS §3.2.1
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:
// 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:
// 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:
- Verify notifications still exist (iOS usually handles this)
- Detect any missed notifications during reboot window
- Reschedule any missing notifications
- Update next run times for repeating schedules
Platform Reference: iOS §3.1.2
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:
// Check if alarms exist and match DB
val alarmsExist = alarmManager.hasAlarm(pendingIntent)
if (alarmsExist && dbMatchesAlarms) {
return WARM_START
}
iOS:
// 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:
// Check if database is empty
val schedules = database.scheduleDao().getEnabled()
if (schedules.isEmpty()) {
return NONE
}
iOS:
// 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
- Notification Persistence: iOS automatically persists notifications across termination and reboot
- No Force Stop: iOS doesn't have user-facing force stop, reducing complexity
- Simplified Recovery: Less recovery needed due to OS persistence
5.2 iOS Challenges
- No Code Execution on Fire: App code doesn't run when notification fires
- Background Limits: Severely limited background execution
- Timing Tolerance: ±180 second tolerance for calendar triggers
5.3 Android Advantages
- Code Execution on Fire: PendingIntent can execute code when alarm fires
- WorkManager: More reliable background execution
- Exact Timing: Can achieve exact timing with permission
5.4 Android Challenges
- No Persistence: Alarms don't persist across reboot
- Force Stop: Hard kill that cannot be bypassed
- Boot Recovery: Must implement boot receiver
6. Testing Strategy
6.1 Scenario Testing
Cold Start:
- Terminate app (swipe away)
- Wait for notification time to pass
- Launch app
- Verify missed notification detection
- Verify future notifications rescheduled
Termination:
- Schedule notifications
- Terminate app
- Clear notifications (simulate system clearing)
- Launch app
- Verify full recovery
Boot:
- Schedule notifications
- Reboot device (or simulate)
- Launch app
- Verify notifications still exist
- Verify any missed notifications detected
7. References
- Android Implementation Directive - Android scenarios
- iOS Implementation Directive - iOS scenarios
- Platform Capability Reference - OS-level facts
- Plugin Requirements - Requirements
Document Version: 1.0.0
Last Updated: 2025-12-08
Next Review: After Phase 1 implementation