Implement checkPermissionStatus() and requestNotificationPermissions() methods for iOS plugin, matching Android functionality. Fix compilation errors across plugin files and add comprehensive build/test infrastructure. Key Changes: - Add checkPermissionStatus() and requestNotificationPermissions() methods - Fix 13+ categories of Swift compilation errors (type conversions, logger API, access control, async/await, etc.) - Create DailyNotificationScheduler, DailyNotificationStorage, DailyNotificationStateActor, and DailyNotificationErrorCodes components - Fix CoreData initialization to handle missing model gracefully for Phase 1 - Add iOS test app build script with simulator auto-detection - Update directive with lessons learned from build and permission work Build Status: ✅ BUILD SUCCEEDED Test App: ✅ Ready for iOS Simulator testing Files Modified: - doc/directives/0003-iOS-Android-Parity-Directive.md (lessons learned) - ios/Plugin/DailyNotificationPlugin.swift (Phase 1 methods) - ios/Plugin/DailyNotificationModel.swift (CoreData fix) - 11+ other plugin files (compilation fixes) Files Added: - ios/Plugin/DailyNotificationScheduler.swift - ios/Plugin/DailyNotificationStorage.swift - ios/Plugin/DailyNotificationStateActor.swift - ios/Plugin/DailyNotificationErrorCodes.swift - scripts/build-ios-test-app.sh - scripts/setup-ios-test-app.sh - test-apps/ios-test-app/ (full test app) - Multiple Phase 1 documentation files
581 lines
14 KiB
Markdown
581 lines
14 KiB
Markdown
# 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
|
|
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
|
<array>
|
|
<string>com.timesafari.dailynotification.fetch</string>
|
|
<string>com.timesafari.dailynotification.notify</string>
|
|
</array>
|
|
```
|
|
|
|
### 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
|
|
|