feat(ios): implement Phase 1 permission methods and fix build issues
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
This commit is contained in:
580
doc/IOS_PHASE1_TESTING_GUIDE.md
Normal file
580
doc/IOS_PHASE1_TESTING_GUIDE.md
Normal file
@@ -0,0 +1,580 @@
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user