# iOS Troubleshooting Guide **Author**: Matthew Raymer **Date**: 2025-12-08 **Status**: 🎯 **ACTIVE** - iOS Troubleshooting Reference **Version**: 1.0.0 ## Purpose This guide provides solutions to common iOS-specific issues when using the Daily Notification Plugin. It covers debugging techniques, common problems, and their solutions. **Reference**: - [iOS Implementation Directive](./ios-implementation-directive.md) - Implementation details - [Platform Capability Reference](./alarms/01-platform-capability-reference.md) - iOS OS behaviors - [iOS Logging Guide](../doc/test-app-ios/IOS_LOGGING_GUIDE.md) - How to view logs --- ## 1. Common Issues ### 1.1 Notifications Not Firing **Symptoms:** - Notifications scheduled but don't appear - No notification at scheduled time - Notifications work in simulator but not on device **Diagnosis Steps:** 1. **Check Notification Permission:** ```swift // In app code UNUserNotificationCenter.current().getNotificationSettings { settings in print("Authorization status: \(settings.authorizationStatus)") } ``` Or check in Xcode Console: ``` DNP-PLUGIN: Notification permission status: authorized ``` 2. **Check Pending Notifications:** ```swift UNUserNotificationCenter.current().getPendingNotificationRequests { requests in print("Pending notifications: \(requests.count)") } ``` 3. **Check Background App Refresh:** - Settings → [Your App] → Background App Refresh - Must be enabled for background tasks 4. **Check Notification Limit:** - iOS limits to 64 pending notifications - Check if limit is exceeded **Solutions:** - **Permission Denied:** - Request permission: `DailyNotification.requestNotificationPermission()` - Guide user to Settings → [Your App] → Notifications - **Background App Refresh Disabled:** - Guide user to enable: Settings → [Your App] → Background App Refresh - Or use: `DailyNotification.openBackgroundAppRefreshSettings()` - **Notification Limit Exceeded:** - Reduce number of scheduled notifications - Implement notification cleanup logic - Check rolling window implementation - **Simulator vs Device:** - Simulator may not fire notifications reliably - Test on physical device for accurate behavior --- ### 1.2 Background Tasks Not Executing **Symptoms:** - Prefetch tasks don't run - Background fetch never executes - BGTaskScheduler tasks not firing **Diagnosis Steps:** 1. **Check BGTaskScheduler Registration:** ```swift // Verify registration in AppDelegate BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.timesafari.dailynotification.fetch", using: nil) { task in // Handler should be registered } ``` 2. **Check Info.plist:** ```xml BGTaskSchedulerPermittedIdentifiers com.timesafari.dailynotification.fetch ``` 3. **Check Background App Refresh:** - Must be enabled in Settings - System-controlled timing (not guaranteed) 4. **Check Logs:** ``` DNP-FETCH-START: Background fetch task started ``` **Solutions:** - **Not Registered:** - Verify registration in `AppDelegate.application(_:didFinishLaunchingWithOptions:)` - Check Info.plist has correct identifiers - **Background App Refresh Disabled:** - User must enable in Settings - Cannot be programmatically enabled - **System Not Executing:** - BGTaskScheduler is system-controlled - Execution timing is not guaranteed - System may defer or skip tasks - Use for prefetching only, not critical scheduling - **Simulator Limitations:** - Background tasks may not execute in simulator - Test on physical device --- ### 1.3 Notifications Disappear After App Termination **Symptoms:** - Notifications scheduled but disappear when app is terminated - Notifications don't persist across app restarts **Diagnosis:** **This should NOT happen on iOS** - notifications persist automatically (OS-guaranteed). **If it happens, check:** 1. **Notification Trigger Type:** - Calendar/time triggers persist ✅ - Location triggers do NOT persist ❌ 2. **Notification Content:** - Ensure notification has valid content - Check for invalid trigger dates 3. **System Storage:** - iOS may clear notifications if device storage is full - Check available storage **Solutions:** - **Use Calendar Triggers:** ```swift // ✅ Persists across reboot let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true) // ❌ Does NOT persist let trigger = UNLocationNotificationTrigger(region: region, repeats: true) ``` - **Check Device Storage:** - Free up storage if device is full - iOS may clear notifications when storage is critical --- ### 1.4 Recovery Not Working **Symptoms:** - Missed notifications not detected on app launch - Future notifications not verified/rescheduled - No recovery activity in logs **Diagnosis:** **Recovery features are NOT yet implemented** (as of 2025-12-08). **Expected Behavior (Once Implemented):** 1. **Check Logs for Recovery:** ``` DNP-REACTIVATION: Starting app launch recovery DNP-REACTIVATION: Detected scenario: coldStart DNP-REACTIVATION: Missed notifications detected: 2 DNP-REACTIVATION: Future notifications verified: 1 ``` 2. **Verify Recovery Logic:** - Should run in `DailyNotificationPlugin.load()` - Should detect missed notifications - Should verify future notifications **Solutions:** - **Implementation Pending:** - See [iOS Implementation Directive Phase 1](./ios-implementation-directive-phase1.md) - Recovery features need to be implemented - **Manual Workaround:** - Check pending notifications manually - Reschedule if needed --- ### 1.5 Database/Storage Issues **Symptoms:** - Database errors in logs - Data not persisting - Core Data errors **Diagnosis Steps:** 1. **Check Database Path:** ```swift let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let dbPath = documentsPath.appendingPathComponent("daily_notifications.db") print("Database path: \(dbPath.path)") ``` 2. **Check Core Data Stack:** - Verify `NSPersistentContainer` initialization - Check for migration errors 3. **Check Logs:** ``` DNP-STORAGE: Database opened successfully DNP-STORAGE: Error opening database: [error] ``` **Solutions:** - **Database Path Issues:** - Verify app has write permissions - Check Documents directory is accessible - Ensure path is correct - **Core Data Errors:** - Check Core Data model version - Verify migration policies - Check for schema mismatches - **Storage Full:** - Free up device storage - iOS may clear app data if storage is critical --- ### 1.6 Permission Issues **Symptoms:** - Permission requests fail - Permission status incorrect - Cannot schedule notifications **Diagnosis:** 1. **Check Current Status:** ```swift UNUserNotificationCenter.current().getNotificationSettings { settings in switch settings.authorizationStatus { case .authorized: // ✅ Can schedule case .denied: // ❌ User denied case .notDetermined: // ⚠️ Not requested yet case .provisional: // ⚠️ Provisional (iOS 12+) @unknown default: break } } ``` 2. **Check Logs:** ``` DNP-PLUGIN: Notification permission status: denied ``` **Solutions:** - **Permission Denied:** - Cannot request again programmatically - Guide user to Settings → [Your App] → Notifications - Use: `DailyNotification.openNotificationSettings()` - **Not Determined:** - Request permission: `DailyNotification.requestNotificationPermission()` - Show explanation before requesting - **Provisional:** - iOS 12+ feature - Notifications delivered quietly - User can upgrade to full permission --- ## 2. Debugging Techniques ### 2.1 Viewing Logs **Xcode Console (Recommended):** 1. Run app in Xcode (Cmd+R) 2. Open Debug Area (Cmd+Shift+Y) 3. Filter by: `DNP-` or `DailyNotification` **Console.app:** 1. Open Console.app 2. Select device/simulator 3. Filter by process: `ios-test-app` **Command Line:** ```bash # Simulator logs xcrun simctl spawn log stream --level=debug --predicate 'processImagePath contains "ios-test-app"' # Device logs (requires device connected) xcrun devicectl device process monitor --device --filter "ios-test-app" ``` ### 2.2 Checking Pending Notifications **Via Plugin Method:** ```typescript const result = await DailyNotification.getPendingNotifications(); console.log(`Pending: ${result.count}`); ``` **Via Swift Code:** ```swift UNUserNotificationCenter.current().getPendingNotificationRequests { requests in print("Pending notifications: \(requests.count)") for request in requests { print(" - \(request.identifier): \(request.content.title)") } } ``` ### 2.3 Checking Background Task Status **Via Plugin Method:** ```typescript const status = await DailyNotification.getBackgroundTaskStatus(); console.log(`Fetch task registered: ${status.fetchTaskRegistered}`); console.log(`Background refresh enabled: ${status.backgroundRefreshEnabled}`); ``` **Via Swift Code:** ```swift // Check registration let registered = BGTaskScheduler.shared.registeredTaskIdentifiers print("Registered tasks: \(registered)") // Check Background App Refresh (requires entitlement) // Cannot check programmatically - must guide user to Settings ``` ### 2.4 Simulating Background Tasks (Simulator Only) **LLDB Command in Xcode:** ```lldb e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.timesafari.dailynotification.fetch"] ``` **Note:** This only works in simulator, not on physical devices. --- ## 3. Platform-Specific Considerations ### 3.1 Simulator vs Device **Simulator Limitations:** - Background tasks may not execute reliably - Notifications may not fire at exact time - Some features require physical device **Device Testing:** - More accurate behavior - Background tasks execute (system-controlled) - Notifications fire reliably **Recommendation:** Test critical features on physical device. ### 3.2 iOS Version Differences **iOS 12+:** - Provisional notification authorization - Background task improvements **iOS 13+:** - State actor support (concurrency) - Improved background execution **iOS 14+:** - Notification interruption levels - Focus modes (may affect notifications) **iOS 15+:** - Notification summary - Focus mode integration ### 3.3 Background Execution Limits **iOS Constraints:** - BGTaskScheduler is system-controlled - Execution timing not guaranteed - Minimum intervals between tasks (hours) - Tasks may be deferred or skipped **Workaround:** - Use BGTaskScheduler for prefetching only - Don't rely on it for critical scheduling - Use UNUserNotificationCenter for notifications (more reliable) --- ## 4. Error Codes ### 4.1 Common Error Codes | Error Code | Description | Solution | | ---------- | ----------- | -------- | | `NOTIFICATION_PERMISSION_DENIED` | User denied notification permission | Guide user to Settings | | `BACKGROUND_REFRESH_DISABLED` | Background App Refresh disabled | Guide user to enable in Settings | | `PENDING_NOTIFICATION_LIMIT_EXCEEDED` | Exceeded 64 notification limit | Reduce scheduled notifications | | `BG_TASK_NOT_REGISTERED` | Background task not registered | Check Info.plist and AppDelegate | | `BG_TASK_EXECUTION_FAILED` | Background task execution failed | Check logs for specific error | ### 4.2 Checking Error Details **Via Logs:** ``` DNP-ERROR: [Error Code] [Error Message] DNP-ERROR: NOTIFICATION_PERMISSION_DENIED: User denied notification permission ``` **Via Plugin:** ```typescript try { await DailyNotification.scheduleDailyNotification({...}); } catch (error) { console.error('Error code:', error.code); console.error('Error message:', error.message); } ``` --- ## 5. Performance Issues ### 5.1 Slow Notification Scheduling **Symptoms:** - Scheduling takes too long - App freezes during scheduling **Solutions:** - Schedule notifications asynchronously - Batch operations when possible - Use background queue for heavy operations ### 5.2 High Memory Usage **Symptoms:** - App memory usage high - Memory warnings in logs **Solutions:** - Implement notification cleanup - Limit cached notifications - Use efficient data structures ### 5.3 Battery Drain **Symptoms:** - Battery drains quickly - Background activity high **Solutions:** - Limit background task frequency - Optimize prefetch operations - Use efficient scheduling algorithms --- ## 6. Getting Help ### 6.1 Log Collection **Collect Logs:** 1. Reproduce the issue 2. Collect logs from Xcode Console or Console.app 3. Filter by `DNP-` prefix 4. Include relevant error messages **Log Format:** ``` DNP-PLUGIN: [Message] DNP-ERROR: [Error Code] [Error Message] DNP-REACTIVATION: [Recovery Activity] ``` ### 6.2 Issue Reporting **Include:** - iOS version - Device model (or simulator) - Plugin version - Steps to reproduce - Relevant logs - Expected vs actual behavior ### 6.3 Documentation References - [iOS Implementation Directive](./ios-implementation-directive.md) - [Platform Capability Reference](./alarms/01-platform-capability-reference.md) - [API Reference](../API.md) - [iOS Logging Guide](../doc/test-app-ios/IOS_LOGGING_GUIDE.md) --- ## 7. Quick Reference ### 7.1 Common Commands **Check Pending Notifications:** ```bash # Via plugin method (recommended) # Or check logs for scheduling activity ``` **View Logs:** ```bash # Xcode Console (Cmd+Shift+Y) # Filter: DNP- # Console.app # Filter: ios-test-app ``` **Check Permissions:** ```typescript const status = await DailyNotification.getNotificationPermissionStatus(); ``` **Open Settings:** ```typescript await DailyNotification.openNotificationSettings(); await DailyNotification.openBackgroundAppRefreshSettings(); ``` ### 7.2 Checklist **Before Reporting Issue:** - [ ] Checked notification permissions - [ ] Verified Background App Refresh is enabled - [ ] Checked pending notification count (< 64) - [ ] Reviewed logs for errors - [ ] Tested on physical device (not just simulator) - [ ] Verified iOS version compatibility - [ ] Checked device storage availability --- **Document Version**: 1.0.0 **Last Updated**: 2025-12-08 **Next Review**: After Phase 1 implementation