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
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)andscheduleImmediateFetch() - iOS: Has
DailyNotificationBackgroundTaskManagerwithscheduleBackgroundTask()method - iOS Pattern: Uses
BGTaskSchedulerwithBGAppRefreshTaskRequest
Recommendation: Defer to Phase 2, Use Placeholder Pattern
Rationale:
- Phase 1 Focus: Core rollover functionality (scheduling next notification)
- Prefetch is Separate: Prefetch scheduling is independent of rollover
- Existing Infrastructure: iOS already has background task infrastructure
- Android Pattern: Android also separates rollover from prefetch (optional parameter)
Implementation Approach
Phase 1 (Current):
- Make
fetcherparameter optional inscheduleNextNotification() - Add TODO comments for Phase 2 integration
- Log prefetch scheduling intent (even if not executed)
Phase 2 (Future):
- Create
DailyNotificationFetcherclass (iOS equivalent) - Integrate with
DailyNotificationBackgroundTaskManager - Use
BGTaskSchedulerfor 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
CAPBridgeViewControllerto access plugins - Test App: Already uses this pattern for other operations
- Production Apps: May have different AppDelegate structures
Recommendation: Use Notification Center Pattern
Rationale:
- Decoupling: AppDelegate doesn't need direct plugin reference
- Flexibility: Works across different app architectures
- Reliability: Notification center is always available
- 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:
DailyNotificationReactivationManageralready 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:
- iOS Limitations: Background tasks are unreliable (system-controlled)
- Existing Infrastructure: Recovery manager already handles app launch scenarios
- Coverage: AppDelegate (foreground) + Recovery (background) covers all cases
- Simplicity: Fewer moving parts = fewer failure points
Implementation Approach
Two Detection Mechanisms:
- Foreground: AppDelegate
willPresent→ immediate rollover - 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:
- Background Operation: Rollover is background, shouldn't interrupt user
- Recovery Available: Recovery manager will catch missed rollovers on next app launch
- Consistency: Matches Android and existing iOS recovery patterns
- User Experience: Silent failures, recovery on next launch
Implementation Approach
Error Handling Strategy:
- Log Errors: Comprehensive logging for debugging
- Continue Execution: Don't crash or interrupt app
- No Retry: Let recovery manager handle on next launch
- No User Notification: Background operation, silent failure
- 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:
- Low Volume: Typically 1 notification per day, batching unnecessary
- Simplicity: Individual processing is simpler and easier to debug
- Error Isolation: Individual processing isolates failures
- 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:
- Phase 1 Focus: Core functionality, manual testing sufficient
- Complexity: Rollover involves system notifications (hard to test automatically)
- Time Investment: Automated tests take time, manual testing faster for Phase 1
- 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)
- ✅ Foreground Delivery: App running, notification fires → rollover triggers
- ✅ Background Delivery: App not running, notification fires → rollover on launch
- ✅ Duplicate Prevention: Multiple rollover attempts → only one scheduled
- ✅ DST Transition: Schedule on DST day → correct time calculation
- ✅ 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:
- Fetcher Parameter: Make optional, add Phase 2 TODOs
- AppDelegate Pattern: Use Notification Center instead of Capacitor bridge
- Background Task: Remove dedicated background task, rely on recovery
- Error Handling: Add comprehensive logging, non-fatal errors
- Performance: Individual processing (no batching)
- Testing: Manual testing checklist for Phase 1
Next Steps
- ✅ Decisions Made (This document)
- Update Review Document with decisions
- Update Implementation Plan with specific patterns
- 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