Updated iOS test app documentation to reflect recent implementation work: channel methods, permission methods, BGTaskScheduler simulator limitation, and plugin discovery troubleshooting. Changes: - Added channel methods (isChannelEnabled, openChannelSettings) to UI mapping - Fixed permission method name (requestPermissions → requestNotificationPermissions) - Added checkPermissionStatus to UI mapping - Added Channel Management section explaining iOS limitations - Added BGTaskScheduler simulator limitation documentation (Code=1 is expected) - Added plugin discovery troubleshooting section (CAPBridgedPlugin conformance) - Added permission and channel methods to behavior classification table - Updated Known OS Limitations with simulator-specific BGTaskScheduler behavior Files modified: - doc/test-app-ios/IOS_TEST_APP_REQUIREMENTS.md: UI mapping, debugging scenarios - doc/test-app-ios/IOS_PREFETCH_TESTING.md: Known limitations, behavior classification
19 KiB
iOS Test App Requirements
Purpose: What the iOS test app must provide so that the testing guide can be executed with parity vs Android
Plugin Target: DailyNotificationPlugin v3.x (iOS)
Phase: Phase 1 – Prefetch MVP
Status: 📋 REQUIRED FOR PHASE 1
Date: 2025-11-15
Author: Matthew Raymer
Directive Reference: doc/directives/0003-iOS-Android-Parity-Directive.md
Note: This app exists to support the prefetch testing scenarios in doc/test-app-ios/IOS_PREFETCH_TESTING.md.
Android parity: Behavior is aligned with test-apps/android-test-app where platform constraints allow. Timing and BGTask heuristics will differ from Android's exact alarms:
- Android: Exact alarms via AlarmManager / WorkManager
- iOS: Heuristic BGTaskScheduler (see glossary); no hard guarantee of 5-min prefetch
Glossary: See glossary in IOS_PREFETCH_TESTING.md for terminology.
Overview
This document defines the requirements for the iOS test app (test-apps/ios-test-app/) that must be created as part of Phase 1 implementation. The iOS test app must provide UI parity with the Android test app (test-apps/android-test-app/) while respecting iOS-specific constraints and capabilities.
Non-Goals (Phase 1)
Out of scope for Phase 1:
- ❌ No full UX polish (color, branding)
- ❌ No localization / accessibility guarantees (text only for internal QA)
- ❌ No production signing / App Store deployment
- ❌ No advanced UI features beyond basic functionality testing
Security & Privacy Constraints
Critical requirements for test app implementation:
- Test app MUST use non-production endpoints and credentials
- JWT / API keys used here are test-only and may be rotated or revoked at any time
- Logs MUST NOT include real user PII (names, emails, phone numbers)
- Any screenshots or shared logs should be scrubbed of secrets before external sharing
- Test app should clearly indicate it's a development/testing build (not production)
If You Only Have 30 Minutes
Quick setup checklist:
- Copy HTML/JS from Android test app (
test-apps/android-test-app/app/src/main/assets/public/index.html) - Wire plugin into Capacitor (
capacitor.config.json) - Add Info.plist keys (BGTask identifiers, background modes, notification permissions)
- Build/run (
./scripts/build-ios-test-app.sh --simulatoror Xcode) - Press buttons: Check Plugin Status → Request Permissions → Schedule Test Notification
- See logs with prefixes
DNP-PLUGIN,DNP-FETCH,DNP-SCHEDULER
UI Parity Requirements
HTML/JS UI
The iOS test app MUST use the same HTML/JS UI as the Android test app to ensure consistent testing experience across platforms.
Source: Copy from test-apps/android-test-app/app/src/main/assets/public/index.html
Required UI Elements:
- Plugin registration status indicator
- Permission status display (✅/❌ indicators)
- Test notification button
- Check permissions button
- Request permissions button
- Channel management buttons (Check Channel Status, Open Channel Settings)
- Status display area
- Log output area (optional, for debugging)
UI Functionality
The test app UI must support:
-
Plugin Status Check
- Display plugin availability status
- Show "Plugin is loaded and ready!" when available
-
Permission Management
- Display current permission status
- Request permissions button
- Check permissions button
- Show ✅/❌ indicators for each permission
-
Channel Management (iOS parity with Android)
- Check channel status button (iOS: checks app-wide notification authorization)
- Open channel settings button (iOS: opens app Settings, not per-channel)
- Note: iOS doesn't have per-channel control like Android; these methods provide app-wide equivalents
-
Notification Testing
- Schedule test notification button
- Display scheduled time
- Show notification status
-
Status Display
- Show last notification time
- Show pending notification count
- Display error messages if any
UI Elements to Plugin Methods Mapping
| UI Element / Button | Plugin Method / API Call | Notes |
|---|---|---|
| "Check Plugin Status" | DailyNotification.configure() or status call |
Verify plugin load & config |
| "Check Permissions" | checkPermissionStatus() |
Returns current notification permission status |
| "Request Permissions" | requestNotificationPermissions() |
Requests notification permissions (shows system dialog) |
| "Schedule Test Notification" | scheduleDailyNotification() |
Should schedule prefetch + notify |
| "Show Last Notification" | getLastNotification() |
Uses deterministic path (Bucket A) |
| "Cancel All Notifications" | cancelAllNotifications() |
Uses deterministic path (Bucket A) |
| "Get Notification Status" | getNotificationStatus() |
Uses deterministic path (Bucket A) |
| "Check Channel Status" | isChannelEnabled(channelId?) |
Checks if notifications enabled (iOS: app-wide) |
| "Open Channel Settings" | openChannelSettings(channelId?) |
Opens notification settings (iOS: app Settings) |
See IOS_PREFETCH_TESTING.md Behavior Classification for deterministic vs heuristic methods.
iOS Permissions Configuration
Info.plist Requirements
The test app's Info.plist MUST include:
<!-- Background Task Identifiers -->
<!-- These identifiers MUST match exactly the values used in IOS_PREFETCH_TESTING.md and the plugin Swift code, otherwise BGTaskScheduler (see glossary) will silently fail -->
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.timesafari.dailynotification.fetch</string> <!-- Prefetch task (BGAppRefreshTask - see glossary) -->
<string>com.timesafari.dailynotification.notify</string> <!-- Notification maintenance task (if applicable) -->
</array>
<!-- Background Modes -->
<key>UIBackgroundModes</key>
<array>
<string>background-fetch</string>
<string>background-processing</string>
<string>remote-notification</string>
</array>
<!-- Notification Permissions -->
<key>NSUserNotificationsUsageDescription</key>
<string>This app uses notifications to deliver daily updates and reminders.</string>
Background App Refresh
- Background App Refresh must be enabled in Settings
- Test app should check and report Background App Refresh status
- User should be guided to enable Background App Refresh if disabled
Build Options
Xcode GUI Build
-
Open Workspace:
cd test-apps/ios-test-app open App.xcworkspace # or App.xcodeproj -
Select Target:
- Choose iOS Simulator (iPhone 15, iPhone 15 Pro, etc.)
- Or physical device (requires signing)
-
Build and Run:
- Press Cmd+R
- Or Product → Run
Command-Line Build
Use the build script:
# From repo root
./scripts/build-ios-test-app.sh --simulator
# Or for device
./scripts/build-ios-test-app.sh --device
Future CI Integration (Optional)
Note for Phase 2+: Consider adding xcodebuild-based CI job that:
- Builds
test-apps/ios-test-appfor simulator - Runs a minimal UI test that:
- Launches app
- Calls
configure()andgetNotificationStatus()
- This ensures test app remains buildable as plugin evolves
Phase 1: Manual testing only; CI integration is out of scope.
Build Requirements
Required Tools:
- Xcode: 15.0 or later
- macOS: 13.0 (Ventura) or later
- iOS Deployment Target: iOS 15.0 or later
- CocoaPods: >= 1.13 (must run
pod installbefore first build) - Node.js: 20.x (recommended)
- npm: Latest stable (comes with Node.js)
- Xcode Command Line Tools: Must run
xcode-select --installif not already installed
Note: Mismatched versions are out of scope for Phase 1 support. Use recommended versions to avoid compatibility issues.
Capacitor Configuration
Plugin Registration
The test app MUST register the DailyNotification plugin:
capacitor.config.json or capacitor.config.ts:
{
"plugins": {
"DailyNotification": {
"enabled": true
}
}
}
Plugin Path
The plugin must be accessible from the test app:
- Development: Plugin source at
../../ios/Plugin/ - Production: Plugin installed via npm/CocoaPods
Sync Command
After making changes to plugin or web assets:
cd test-apps/ios-test-app
npx cap sync ios
Debugging Strategy
When executing the scenarios in IOS_PREFETCH_TESTING.md, use the following commands while looking for logs with prefixes DNP-PLUGIN, DNP-FETCH, DNP-SCHEDULER.
Xcode Debugger
Check Pending Notifications:
po UNUserNotificationCenter.current().pendingNotificationRequests()
Check Permission Status:
po await UNUserNotificationCenter.current().notificationSettings()
Manually Trigger BGTask (Simulator Only):
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.timesafari.dailynotification.fetch"]
Console.app Logging
- Open Console.app (Applications → Utilities)
- Select device/simulator
- Filter by:
DNP-orDailyNotification
Key Log Prefixes:
DNP-PLUGIN:- Main plugin operationsDNP-FETCH:- Background fetch operationsDNP-SCHEDULER:- Scheduling operationsDNP-STORAGE:- Storage operations
Common Debugging Scenarios
Scenario: BGTask not running when expected → follow this checklist:
-
BGTask Not Running:
- Check Info.plist has
BGTaskSchedulerPermittedIdentifiers - Verify identifiers match code exactly (case-sensitive)
- Verify task registered in AppDelegate before app finishes launching
- Check Background App Refresh enabled in Settings → [Your App]
- Verify app not force-quit (iOS won't run BGTask for force-quit apps)
- Use simulator-only LLDB command to manually trigger
- See also:
IOS_PREFETCH_TESTING.md – Log Checklist: Section 3
- Check Info.plist has
-
Notifications Not Delivering:
- Check notification permissions:
UNUserNotificationCenter.current().getNotificationSettings() - Verify notification scheduled:
UNUserNotificationCenter.current().getPendingNotificationRequests() - Check notification category registered
- See also:
IOS_PREFETCH_TESTING.md – Log Checklist: Section 4
- Check notification permissions:
-
BGTaskScheduler Fails on Simulator:
- Expected Behavior: BGTaskSchedulerErrorDomain Code=1 (notPermitted) is normal on simulator
- BGTaskScheduler doesn't work reliably on simulator - this is an iOS limitation, not a plugin bug
- Notification scheduling still works; prefetch won't run on simulator but will work on real devices
- Error handling logs clear message: "Background fetch scheduling failed (expected on simulator)"
- See also:
IOS_PREFETCH_TESTING.md – Known OS Limitationsfor details - Testing: Use Xcode → Debug → Simulate Background Fetch for simulator testing
-
Plugin Not Discovered:
- Check plugin class conforms to
CAPBridgedPluginprotocol (required for Capacitor discovery) - Verify
@objc extension DailyNotificationPlugin: CAPBridgedPluginexists in plugin code - Check plugin framework is force-loaded in AppDelegate before Capacitor initializes
- Verify
pluginMethodsarray includes all@objcmethods - See also:
doc/directives/0003-iOS-Android-Parity-Directive.md – Plugin Discovery Issuefor detailed troubleshooting
- Check plugin class conforms to
-
Build Failures:
- Run
pod install - Clean build folder (Cmd+Shift+K)
- Verify Capacitor plugin path
- Run
Test App Implementation Checklist
Setup
- Create
test-apps/ios-test-app/directory - Initialize Capacitor iOS project
- Copy HTML/JS UI from Android test app
- Configure Info.plist with BGTask identifiers
- Configure Info.plist with background modes
- Add notification permission description
Plugin Integration
- Register DailyNotification plugin in Capacitor config
- Ensure plugin path is correct
- Run
npx cap sync ios - Verify plugin loads in test app
UI Implementation
- Copy HTML/JS from Android test app
- Test plugin status display
- Test permission status display
- Test notification scheduling UI
- Test status display
Build & Test
- Build script works (
./scripts/build-ios-test-app.sh) - App builds in Xcode
- App runs on simulator
- Plugin methods work from UI
- Notifications deliver correctly
- BGTask executes (with manual trigger in simulator)
File Structure
test-apps/ios-test-app/
├── App.xcworkspace # Xcode workspace (if using CocoaPods)
├── App.xcodeproj # Xcode project
├── App/ # Main app directory
│ ├── App/
│ │ ├── AppDelegate.swift
│ │ ├── SceneDelegate.swift
│ │ ├── Info.plist # Must include BGTask identifiers
│ │ └── Assets.xcassets
│ └── Public/ # Web assets (HTML/JS)
│ └── index.html # Same as Android test app
├── Podfile # CocoaPods dependencies
├── capacitor.config.json # Capacitor configuration
└── package.json # npm dependencies (if any)
Testing Scenarios
Basic Functionality
Happy Path Example:
When everything is working correctly, your first run should produce logs like this:
[DNP-PLUGIN] configure() called
[DNP-PLUGIN] status = ready
[DNP-PLUGIN] requestPermissions() → granted
[DNP-SCHEDULER] scheduleDailyNotification() → notificationTime=2025-11-15T05:53:00Z
[DNP-FETCH] Scheduling prefetch for notification at 2025-11-15T05:53:00Z
[DNP-FETCH] BGAppRefreshTask scheduled (earliestBeginDate=2025-11-15T05:48:00Z)
[DNP-SCHEDULER] Scheduling notification for 2025-11-15T05:53:00Z
[DNP-STORAGE] Persisted schedule to DB (id=..., type=DAILY, ...)
If your first run doesn't look roughly like this, fix that before proceeding to BGTask tests.
-
Plugin Registration
[P1-Core][SIM+DEV]- Launch app
- Verify plugin status shows "Plugin is loaded and ready!"
- See also:
IOS_PREFETCH_TESTING.md – Simulator Test Plan: Step 2
-
Permission Management
[P1-Core][SIM+DEV]- Check permissions
- Request permissions
- Verify permissions granted
- See also:
IOS_PREFETCH_TESTING.md – Simulator Test Plan: Step 6 (Negative-Path Tests)
-
Notification Scheduling
[P1-Prefetch][SIM+DEV]- Schedule test notification
- Verify notification scheduled
- Wait for notification to appear
- See also:
IOS_PREFETCH_TESTING.md – Simulator Test Plan: Steps 3-5
Background Tasks
Sim vs Device Behavior:
[SIM+DEV]– can be fully tested on simulator and device (logic correctness)[DEV-ONLY]– timing / heuristic behavior, must be verified on real hardware
-
BGTask Scheduling
[P1-Prefetch][SIM+DEV]- Schedule notification with prefetch
- Verify BGTask scheduled 5 minutes before notification
- Manually trigger BGTask (simulator only)
- Verify content fetched
- Note: Logic and logs on sim; real timing on device
- See also:
IOS_PREFETCH_TESTING.md – Simulator Test Plan: Steps 3-4
-
BGTask Miss Detection
[P1-Prefetch][DEV-ONLY]- Schedule notification
- Wait 15+ minutes
- Launch app
- Verify BGTask rescheduled
- Note: Only meaningful on device (heuristic timing)
- See also:
IOS_PREFETCH_TESTING.md – Real Device Test Plan: Step 4
Error Handling
-
Permission Denied
[P1-Core]- Deny notification permissions
- Try to schedule notification
- Verify error returned
- See also:
IOS_PREFETCH_TESTING.md – Simulator Test Plan: Step 6 (Negative-Path Tests)
-
Invalid Parameters
[P1-Core]- Try to schedule with invalid time format
- Verify error returned
Advanced Features [P2-Advanced]
-
Rolling Window
[P2-Advanced]- Schedule multiple notifications
- Verify rolling window maintenance
- Check notification limits (64 max on iOS)
-
TTL Enforcement
[P2-Advanced]- Schedule notification with prefetch
- Wait for TTL to expire
- Verify stale content discarded at delivery
Developer/Test Harness Features (Optional but Recommended)
Since this test app is for internal testing, it can expose more tools:
Dev-Only Toggles
-
Force Schedule Notification N Minutes from Now
- Bypass normal scheduling UI
- Directly call
scheduleDailyNotification()with calculated time - Useful for quick testing scenarios
-
Force "Prefetch-Only" Task
- Trigger BGTask without scheduling notification
- Useful for testing prefetch logic in isolation
- Display raw JSON returned from API (last fetched payload)
-
Display Raw API Response
- Show last fetched payload as JSON
- Useful for debugging API responses
- Can be referenced from
IOS_PREFETCH_TESTING.mdas shortcuts for specific scenarios
-
Manual BGTask Trigger (Dev Build Only)
- Button to manually trigger BGTask (simulator only)
- Wraps the LLDB command in UI for convenience
These features can then be referenced from IOS_PREFETCH_TESTING.md as shortcuts for specific test scenarios.
Risks & Gotchas
Common pitfalls when working with the test app:
-
Accidentally editing shared HTML/JS in iOS test app instead of Android canonical source
- Always edit Android test app HTML/JS first, then copy to iOS
- Keep iOS test app HTML/JS as a copy, not the source of truth
-
Forgetting
npx cap sync iosafter plugin or asset changes- Run
npx cap sync iosafter any plugin code changes - Run
npx cap sync iosafter any web asset changes - Check
capacitor.config.jsonis up to date
- Run
-
Running with stale Pods
- Run
pod repo updateperiodically - Run
pod installafter dependency changes - Clean build folder (Cmd+Shift+K) if Pods seem stale
- Run
-
Changing BGTask identifiers in one place but not the other
- Info.plist
BGTaskSchedulerPermittedIdentifiersmust match Swift code exactly (case-sensitive) - Check both places when updating identifiers
- See
IOS_PREFETCH_TESTING.mdfor identifier requirements
- Info.plist
-
Mismatched tooling versions
- Use recommended versions (Node 20.x, CocoaPods >= 1.13, Xcode 15.0+)
- Mismatched versions are out of scope for Phase 1 support
References
- Directive:
doc/directives/0003-iOS-Android-Parity-Directive.md - Testing Guide:
doc/test-app-ios/IOS_PREFETCH_TESTING.md- Comprehensive prefetch testing procedures - Android Test App:
test-apps/android-test-app/ - Build Script:
scripts/build-ios-test-app.sh
Review & Sign-Off Checklist (Phase 1)
Use this checklist to verify Phase 1 iOS test app is complete:
- Test app builds on simulator with recommended toolchain (Node 20.x, CocoaPods >= 1.13, Xcode 15.0+)
- Test app builds on at least one real device
- All UI buttons map to plugin methods as per the UI Elements to Plugin Methods Mapping table
- Happy-path log sequence matches the example in Testing Scenarios → Basic Functionality
- BGTask identifiers are consistent (Info.plist ↔ Swift ↔ docs)
- Risks & Gotchas section has been read and acknowledged by the implementer
- Security & Privacy Constraints have been followed (non-production endpoints, no PII in logs)
Changelog (high-level)
- 2025-11-15 — Initial Phase 1 version (prefetch MVP, Android parity)
Status: 📋 REQUIRED FOR PHASE 1
Last Updated: 2025-11-15