# iOS Phase 1 Testing Guide **Status:** ✅ **READY FOR TESTING** **Phase:** Phase 1 - Core Infrastructure Parity **Target:** iOS Simulator (primary) or Physical Device --- ## Quick Start Testing ### Prerequisites - **Xcode Version:** 15.0 or later - **macOS Version:** 13.0 (Ventura) or later - **iOS Deployment Target:** iOS 15.0 or later - **Test App:** `test-apps/ios-test-app/` (to be created) ### Testing Environment Setup 1. **Build Test App:** ```bash # From repo root ./scripts/build-ios-test-app.sh --simulator ``` Note: If build script doesn't exist yet, see "Manual Build Steps" below. 2. **Open in Xcode:** ```bash cd test-apps/ios-test-app open App.xcworkspace # or App.xcodeproj ``` 3. **Run on Simulator:** - Select target device (iPhone 15, iPhone 15 Pro, etc.) - Press Cmd+R to build and run - Or use Xcode menu: Product → Run --- ## Phase 1 Test Cases ### Test Case 1: Plugin Initialization **Objective:** Verify plugin loads and initializes correctly **Steps:** 1. Launch test app on iOS Simulator 2. Check Console.app logs for: `DNP-PLUGIN: Daily Notification Plugin loaded on iOS` 3. Verify no initialization errors **Expected Results:** - Plugin loads without errors - Storage and scheduler components initialized - State actor created (iOS 13+) **Logs to Check:** ``` DNP-PLUGIN: Daily Notification Plugin loaded on iOS DailyNotificationStorage: Database opened successfully at [path] DailyNotificationScheduler: Notification category setup complete ``` --- ### Test Case 2: Configure Method **Objective:** Test plugin configuration **JavaScript Test Code:** ```javascript import { DailyNotification } from '@capacitor-community/daily-notification'; // Test configure await DailyNotification.configure({ options: { storage: 'tiered', ttlSeconds: 3600, prefetchLeadMinutes: 5, maxNotificationsPerDay: 1, retentionDays: 7 } }); ``` **Steps:** 1. Call `configure()` with options 2. Check Console.app for: `DNP-PLUGIN: Plugin configuration completed successfully` 3. Verify settings stored in UserDefaults **Expected Results:** - Configuration succeeds without errors - Settings stored correctly - Database path set correctly **Verification:** ```swift // In Xcode debugger or Console.app po UserDefaults.standard.dictionary(forKey: "DailyNotificationPrefs") ``` --- ### Test Case 3: Schedule Daily Notification **Objective:** Test main scheduling method with prefetch **JavaScript Test Code:** ```javascript // Schedule notification for 5 minutes from now const now = new Date(); const scheduleTime = new Date(now.getTime() + 5 * 60 * 1000); const hour = scheduleTime.getHours(); const minute = scheduleTime.getMinutes(); const timeString = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`; await DailyNotification.scheduleDailyNotification({ options: { time: timeString, title: "Test Notification", body: "This is a Phase 1 test notification", sound: true, url: null } }); ``` **Steps:** 1. Schedule notification 5 minutes from now 2. Verify prefetch scheduled 5 minutes before notification time 3. Check Console.app logs 4. Wait for notification to appear **Expected Results:** - Notification scheduled successfully - Prefetch BGTask scheduled 5 minutes before notification - Notification appears at scheduled time (±180s tolerance) **Logs to Check:** ``` DNP-PLUGIN: Scheduling daily notification DNP-PLUGIN: Daily notification scheduled successfully DNP-FETCH-SCHEDULE: Background fetch scheduled for [date] DailyNotificationScheduler: Notification scheduled successfully for [date] ``` **Verification Commands:** ```bash # Check pending notifications (in Xcode debugger) po UNUserNotificationCenter.current().pendingNotificationRequests() # Check BGTask scheduling (simulator only) # Use LLDB command in Xcode debugger: e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.timesafari.dailynotification.fetch"] ``` --- ### Test Case 4: Get Last Notification **Objective:** Test last notification retrieval **JavaScript Test Code:** ```javascript const lastNotification = await DailyNotification.getLastNotification(); console.log('Last notification:', lastNotification); ``` **Steps:** 1. Schedule a notification 2. Wait for it to fire (or manually trigger) 3. Call `getLastNotification()` 4. Verify returned data structure **Expected Results:** - Returns notification object with: `id`, `title`, `body`, `timestamp`, `url` - Returns empty object `{}` if no notifications exist - Thread-safe retrieval via state actor **Expected Response:** ```json { "id": "daily_1234567890", "title": "Test Notification", "body": "This is a Phase 1 test notification", "timestamp": 1234567890000, "url": null } ``` --- ### Test Case 5: Cancel All Notifications **Objective:** Test cancellation of all scheduled notifications **JavaScript Test Code:** ```javascript // Schedule multiple notifications first await DailyNotification.scheduleDailyNotification({...}); await DailyNotification.scheduleDailyNotification({...}); // Then cancel all await DailyNotification.cancelAllNotifications(); ``` **Steps:** 1. Schedule 2-3 notifications 2. Verify they're scheduled: `getNotificationStatus()` 3. Call `cancelAllNotifications()` 4. Verify all cancelled **Expected Results:** - All notifications cancelled - Storage cleared - Pending count = 0 **Logs to Check:** ``` DNP-PLUGIN: All notifications cancelled successfully DailyNotificationScheduler: All notifications cancelled DailyNotificationStorage: All notifications cleared ``` --- ### Test Case 6: Get Notification Status **Objective:** Test status retrieval **JavaScript Test Code:** ```javascript const status = await DailyNotification.getNotificationStatus(); console.log('Status:', status); ``` **Steps:** 1. Call `getNotificationStatus()` 2. Verify response structure 3. Check permission status 4. Check pending count **Expected Results:** - Returns complete status object - Permission status accurate - Pending count accurate - Next notification time calculated **Expected Response:** ```json { "isEnabled": true, "isScheduled": true, "lastNotificationTime": 1234567890000, "nextNotificationTime": 1234567895000, "pending": 1, "settings": { "storageMode": "tiered", "ttlSeconds": 3600 } } ``` --- ### Test Case 7: Update Settings **Objective:** Test settings update **JavaScript Test Code:** ```javascript await DailyNotification.updateSettings({ settings: { sound: false, priority: "high", timezone: "America/New_York" } }); ``` **Steps:** 1. Call `updateSettings()` with new settings 2. Verify settings stored 3. Retrieve settings and verify changes **Expected Results:** - Settings updated successfully - Changes persisted - Thread-safe update via state actor --- ### Test Case 8: BGTask Miss Detection **Objective:** Test BGTask miss detection and rescheduling **Steps:** 1. Schedule a notification with prefetch 2. Note the BGTask `earliestBeginDate` from logs 3. Simulate missing the BGTask window: - Wait 15+ minutes after `earliestBeginDate` - Ensure no successful run recorded 4. Launch app (triggers `checkForMissedBGTask()`) 5. Verify BGTask rescheduled **Expected Results:** - Miss detection triggers on app launch - BGTask rescheduled for 1 minute from now - Logs show: `DNP-FETCH: BGTask missed window; rescheduling` **Logs to Check:** ``` DNP-FETCH: BGTask missed window; rescheduling DNP-FETCH: BGTask rescheduled for [date] ``` **Manual Trigger (Simulator Only):** ```bash # In Xcode debugger (LLDB) e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.timesafari.dailynotification.fetch"] ``` --- ### Test Case 9: Permission Auto-Healing **Objective:** Test automatic permission request **Steps:** 1. Reset notification permissions (Settings → [App] → Notifications → Off) 2. Call `scheduleDailyNotification()` 3. Verify permission request dialog appears 4. Grant permissions 5. Verify scheduling succeeds **Expected Results:** - Permission request dialog appears automatically - Scheduling succeeds after granting - Error returned if permissions denied **Logs to Check:** ``` DailyNotificationScheduler: Permission request result: true DailyNotificationScheduler: Scheduling notification: [id] ``` --- ### Test Case 10: Error Handling **Objective:** Test error code responses **Test Scenarios:** 1. **Missing Parameters:** ```javascript await DailyNotification.scheduleDailyNotification({ options: {} // Missing 'time' parameter }); ``` **Expected Error:** ```json { "error": "missing_required_parameter", "message": "Missing required parameter: time" } ``` 2. **Invalid Time Format:** ```javascript await DailyNotification.scheduleDailyNotification({ options: { time: "invalid" } }); ``` **Expected Error:** ```json { "error": "invalid_time_format", "message": "Invalid time format. Use HH:mm" } ``` 3. **Notifications Denied:** - Deny notification permissions - Try to schedule notification - Verify error code returned **Expected Error:** ```json { "error": "notifications_denied", "message": "Notification permissions denied" } ``` --- ## Manual Build Steps (If Build Script Not Available) ### Step 1: Install Dependencies ```bash cd ios pod install ``` ### Step 2: Open in Xcode ```bash open DailyNotificationPlugin.xcworkspace # or open DailyNotificationPlugin.xcodeproj ``` ### Step 3: Configure Build Settings 1. Select project in Xcode 2. Go to Signing & Capabilities 3. Add Background Modes: - Background fetch - Background processing 4. Add to Info.plist: ```xml BGTaskSchedulerPermittedIdentifiers com.timesafari.dailynotification.fetch com.timesafari.dailynotification.notify ``` ### Step 4: Build and Run - Select target device (Simulator or Physical Device) - Press Cmd+R or Product → Run --- ## Debugging Tools ### Console.app Logging **View Logs:** 1. Open Console.app (Applications → Utilities) 2. Select your device/simulator 3. Filter by: `DNP-` or `DailyNotification` **Key Log Prefixes:** - `DNP-PLUGIN:` - Main plugin operations - `DNP-FETCH:` - Background fetch operations - `DNP-FETCH-SCHEDULE:` - BGTask scheduling - `DailyNotificationStorage:` - Storage operations - `DailyNotificationScheduler:` - Scheduling operations ### Xcode Debugger Commands **Check Pending Notifications:** ```swift po UNUserNotificationCenter.current().pendingNotificationRequests() ``` **Check Permission Status:** ```swift po await UNUserNotificationCenter.current().notificationSettings() ``` **Check BGTask Status (Simulator Only):** ```swift e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.timesafari.dailynotification.fetch"] ``` **Check Storage:** ```swift po UserDefaults.standard.dictionary(forKey: "DailyNotificationPrefs") ``` --- ## Common Issues & Solutions ### Issue 1: BGTaskScheduler Not Running **Symptoms:** - BGTask never executes - No logs from `handleBackgroundFetch()` **Solutions:** 1. Verify Info.plist has `BGTaskSchedulerPermittedIdentifiers` 2. Check task registered in `setupBackgroundTasks()` 3. **Simulator workaround:** Use LLDB command to manually trigger (see above) ### Issue 2: Notifications Not Delivering **Symptoms:** - Notification scheduled but never appears - No notification in notification center **Solutions:** 1. Check permissions: `UNUserNotificationCenter.current().getNotificationSettings()` 2. Verify notification scheduled: `getPendingNotificationRequests()` 3. Check notification category registered 4. Verify time hasn't passed (iOS may deliver immediately if time passed) ### Issue 3: Build Failures **Symptoms:** - Xcode build errors - Missing dependencies **Solutions:** 1. Run `pod install` in `ios/` directory 2. Clean build folder: Product → Clean Build Folder (Cmd+Shift+K) 3. Verify Capacitor plugin path in `capacitor.plugins.json` 4. Check Xcode scheme matches workspace ### Issue 4: Background Tasks Expiring **Symptoms:** - BGTask starts but expires before completion - Logs show: `Background fetch task expired` **Solutions:** 1. Ensure `task.setTaskCompleted(success:)` called before expiration 2. Keep processing efficient (< 30 seconds) 3. Schedule next task immediately after completion --- ## Testing Checklist ### Phase 1 Core Methods - [ ] `configure()` - Configuration succeeds - [ ] `scheduleDailyNotification()` - Notification schedules correctly - [ ] `getLastNotification()` - Returns correct notification - [ ] `cancelAllNotifications()` - All notifications cancelled - [ ] `getNotificationStatus()` - Status accurate - [ ] `updateSettings()` - Settings updated correctly ### Background Tasks - [ ] BGTask scheduled 5 minutes before notification - [ ] BGTask executes successfully - [ ] BGTask miss detection works - [ ] BGTask rescheduling works ### Error Handling - [ ] Missing parameter errors returned - [ ] Invalid time format errors returned - [ ] Permission denied errors returned - [ ] Error codes match Android format ### Thread Safety - [ ] No race conditions observed - [ ] State actor used for all storage operations - [ ] Background tasks use state actor --- ## Next Steps After Testing 1. **Document Issues:** Create GitHub issues for any bugs found 2. **Update Test Cases:** Add test cases for edge cases discovered 3. **Performance Testing:** Test with multiple notifications 4. **Phase 2 Preparation:** Begin Phase 2 advanced features --- ## References - **Directive:** `doc/directives/0003-iOS-Android-Parity-Directive.md` - **Phase 1 Summary:** `doc/PHASE1_COMPLETION_SUMMARY.md` - **Android Testing:** `docs/notification-testing-procedures.md` - **Comprehensive Testing:** `docs/comprehensive-testing-guide-v2.md` --- **Status:** ✅ **READY FOR TESTING** **Last Updated:** 2025-01-XX