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

396 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](./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