Files
daily-notification-plugin/docs/platform/ios/ROLLOVER_QA.md
Matthew Raymer c39bd7cec6 docs: Consolidate documentation structure (139 files, zero information loss)
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
2025-12-18 09:13:18 +00:00

12 KiB

iOS Rollover Implementation — Open Questions & Answers

Date: 2025-01-27
Status: Pre-Implementation Decisions


Question 1: Fetcher Integration

Question: How should we integrate DailyNotificationFetcher for prefetch scheduling? (Phase 2)

Current State Analysis

  • Android: Uses DailyNotificationFetcher.scheduleFetch(fetchTime) and scheduleImmediateFetch()
  • iOS: Has DailyNotificationBackgroundTaskManager with scheduleBackgroundTask() method
  • iOS Pattern: Uses BGTaskScheduler with BGAppRefreshTaskRequest

Recommendation: Defer to Phase 2, Use Placeholder Pattern

Rationale:

  1. Phase 1 Focus: Core rollover functionality (scheduling next notification)
  2. Prefetch is Separate: Prefetch scheduling is independent of rollover
  3. Existing Infrastructure: iOS already has background task infrastructure
  4. Android Pattern: Android also separates rollover from prefetch (optional parameter)

Implementation Approach

Phase 1 (Current):

  • Make fetcher parameter optional in scheduleNextNotification()
  • Add TODO comments for Phase 2 integration
  • Log prefetch scheduling intent (even if not executed)

Phase 2 (Future):

  • Create DailyNotificationFetcher class (iOS equivalent)
  • Integrate with DailyNotificationBackgroundTaskManager
  • Use BGTaskScheduler for prefetch scheduling
  • Calculate fetch time: nextScheduledTime - (5 * 60 * 1000) (5 minutes before)

Code Pattern

// Phase 1: Optional fetcher, log intent
if let fetcher = fetcher {
    let fetchTime = nextScheduledTime - (5 * 60 * 1000)
    // TODO: Phase 2 - Implement fetcher.scheduleFetch(fetchTime)
    print("\(Self.TAG): RESCHEDULE_PREFETCH_SCHEDULED id=\(content.id) next_fetch=\(fetchTime)")
} else {
    print("\(Self.TAG): RESCHEDULE_PREFETCH_SKIP id=\(content.id) fetcher_not_available")
}

Decision: Defer to Phase 2, use optional parameter pattern


Question 2: AppDelegate Access

Question: Is there a better way to access the plugin from AppDelegate without using Capacitor bridge?

Current State Analysis

  • Capacitor Pattern: Uses CAPBridgeViewController to access plugins
  • Test App: Already uses this pattern for other operations
  • Production Apps: May have different AppDelegate structures

Recommendation: Use Notification Center Pattern

Rationale:

  1. Decoupling: AppDelegate doesn't need direct plugin reference
  2. Flexibility: Works across different app architectures
  3. Reliability: Notification center is always available
  4. Testability: Easier to test without Capacitor dependency

Implementation Approach

Option A: Notification Center (Recommended)

  • Plugin registers for notification delivery events
  • AppDelegate posts notification when delivery detected
  • Plugin handles rollover in response to notification

Option B: Capacitor Bridge (Fallback)

  • Use existing bridge pattern
  • Works but creates tight coupling
  • Use as fallback if notification center doesn't work

Code Pattern

// In DailyNotificationPlugin.load():
NotificationCenter.default.addObserver(
    self,
    selector: #selector(handleNotificationDelivery(_:)),
    name: NSNotification.Name("DailyNotificationDelivered"),
    object: nil
)

// In AppDelegate.willPresent:
NotificationCenter.default.post(
    name: NSNotification.Name("DailyNotificationDelivered"),
    object: nil,
    userInfo: [
        "notification_id": notificationId,
        "scheduled_time": scheduledTime
    ]
)

Decision: Use Notification Center pattern, with Capacitor bridge as fallback


Question 3: Background Task

Question: Should we add a dedicated background task for rollover detection, or rely on existing recovery mechanisms?

Current State Analysis

  • Existing Recovery: DailyNotificationReactivationManager already runs on app launch
  • Background Tasks: iOS has strict limits on background execution
  • Reliability: Multiple detection mechanisms increase reliability

Recommendation: Rely on Existing Recovery + AppDelegate

Rationale:

  1. iOS Limitations: Background tasks are unreliable (system-controlled)
  2. Existing Infrastructure: Recovery manager already handles app launch scenarios
  3. Coverage: AppDelegate (foreground) + Recovery (background) covers all cases
  4. Simplicity: Fewer moving parts = fewer failure points

Implementation Approach

Two Detection Mechanisms:

  1. Foreground: AppDelegate willPresent → immediate rollover
  2. Background: Recovery Manager → rollover on app launch

No Dedicated Background Task:

  • Background tasks are unreliable (system decides when to run)
  • Recovery manager already covers app launch scenarios
  • Adding another mechanism adds complexity without significant benefit

Code Pattern

// Detection Mechanism 1: Foreground (AppDelegate)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, ...) {
    // Trigger rollover immediately
    await handleNotificationRollover(...)
}

// Detection Mechanism 2: Background (Recovery Manager)
func performColdStartRecovery() async {
    // Check for delivered notifications
    await checkAndProcessDeliveredNotifications()
}

Decision: Rely on existing recovery + AppDelegate, no dedicated background task


Question 4: Error Handling

Question: How should we handle rollover failures? Retry? Log? User notification?

Current State Analysis

  • Android Pattern: Logs errors, continues execution (non-fatal)
  • iOS Recovery Manager: Catches all errors, logs, continues (non-fatal)
  • User Experience: Failures should be silent (background operation)

Recommendation: Log + Continue (Non-Fatal)

Rationale:

  1. Background Operation: Rollover is background, shouldn't interrupt user
  2. Recovery Available: Recovery manager will catch missed rollovers on next app launch
  3. Consistency: Matches Android and existing iOS recovery patterns
  4. User Experience: Silent failures, recovery on next launch

Implementation Approach

Error Handling Strategy:

  1. Log Errors: Comprehensive logging for debugging
  2. Continue Execution: Don't crash or interrupt app
  3. No Retry: Let recovery manager handle on next launch
  4. No User Notification: Background operation, silent failure
  5. History Recording: Record failures in history (if history implemented)

Code Pattern

func scheduleNextNotification(...) async -> Bool {
    do {
        // Rollover logic
        return true
    } catch {
        print("\(Self.TAG): RESCHEDULE_ERR id=\(content.id) err=\(error.localizedDescription)")
        // Log error but don't throw - let recovery handle on next launch
        return false
    }
}

// In recovery manager:
if !scheduled {
    print("\(Self.TAG): Failed to roll over delivered notification id=\(notificationId)")
    // Recovery will retry on next app launch
}

Decision: Log + Continue (non-fatal), no retry, no user notification


Question 5: Performance

Question: Should we batch rollover operations or process individually?

Current State Analysis

  • Android Pattern: Processes individually (one notification at a time)
  • iOS Recovery: Processes notifications individually
  • Volume: Typically 1-2 notifications per day (low volume)

Recommendation: Process Individually

Rationale:

  1. Low Volume: Typically 1 notification per day, batching unnecessary
  2. Simplicity: Individual processing is simpler and easier to debug
  3. Error Isolation: Individual processing isolates failures
  4. Consistency: Matches Android and existing iOS patterns

Implementation Approach

Individual Processing:

  • Process each notification rollover separately
  • Each rollover is independent operation
  • Failures in one don't affect others
  • Easier to log and debug

Future Optimization (if needed):

  • If volume increases, consider batching
  • Current volume doesn't justify batching complexity

Code Pattern

// Process individually (current approach)
for notification in deliveredNotifications {
    await scheduler.scheduleNextNotification(notification, ...)
}

// Batching would look like:
// await scheduler.scheduleNextNotificationsBatch(notifications, ...)
// But not needed for current volume

Decision: Process individually (current volume doesn't justify batching)


Question 6: Testing

Question: Do we need automated tests for rollover, or is manual testing sufficient for Phase 1?

Current State Analysis

  • Existing Tests: iOS has unit tests for recovery manager
  • Test Coverage: Some components have tests, others don't
  • Phase 1 Scope: Core rollover functionality

Recommendation: Manual Testing for Phase 1, Automated Tests for Phase 2

Rationale:

  1. Phase 1 Focus: Core functionality, manual testing sufficient
  2. Complexity: Rollover involves system notifications (hard to test automatically)
  3. Time Investment: Automated tests take time, manual testing faster for Phase 1
  4. Phase 2: Add automated tests when edge cases are implemented

Implementation Approach

Phase 1 Testing:

  • Manual testing checklist
  • Test scenarios: foreground delivery, background delivery, duplicates
  • Real device testing (simulator may not handle notifications correctly)

Phase 2 Testing:

  • Unit tests for time calculations (DST, timezone)
  • Integration tests for rollover flow
  • Edge case tests (time changes, timezone changes)

Test Checklist (Phase 1)

  1. Foreground Delivery: App running, notification fires → rollover triggers
  2. Background Delivery: App not running, notification fires → rollover on launch
  3. Duplicate Prevention: Multiple rollover attempts → only one scheduled
  4. DST Transition: Schedule on DST day → correct time calculation
  5. Error Handling: Simulate failure → graceful degradation

Decision: Manual testing for Phase 1, automated tests for Phase 2


Summary of Decisions

Question Decision Rationale
Fetcher Integration Defer to Phase 2, optional parameter Prefetch is separate concern, Phase 1 focuses on core rollover
AppDelegate Access Notification Center pattern Decoupling, flexibility, reliability
Background Task Rely on existing recovery iOS limitations, existing infrastructure sufficient
Error Handling Log + Continue (non-fatal) Background operation, recovery handles failures
Performance Process individually Low volume, simplicity, consistency
Testing Manual for Phase 1, automated for Phase 2 Phase 1 scope, complexity, time investment

Implementation Impact

Changes to Review Document

Based on these decisions, the review document should be updated:

  1. Fetcher Parameter: Make optional, add Phase 2 TODOs
  2. AppDelegate Pattern: Use Notification Center instead of Capacitor bridge
  3. Background Task: Remove dedicated background task, rely on recovery
  4. Error Handling: Add comprehensive logging, non-fatal errors
  5. Performance: Individual processing (no batching)
  6. Testing: Manual testing checklist for Phase 1

Next Steps

  1. Decisions Made (This document)
  2. Update Review Document with decisions
  3. Update Implementation Plan with specific patterns
  4. Begin Phase 1 Implementation

References

  • Review Document: docs/ios-rollover-implementation-review.md
  • Edge Case Plan: docs/ios-rollover-edge-case-plan.md
  • Android Implementation: android/src/main/java/com/timesafari/dailynotification/DailyNotificationWorker.java
  • iOS Recovery Manager: ios/Plugin/DailyNotificationReactivationManager.swift
  • iOS Background Tasks: ios/Plugin/DailyNotificationBackgroundTaskManager.swift