Files
daily-notification-plugin/doc/test-app-ios/IOS_TEST_APP_REQUIREMENTS.md
Matthew Raymer 95507c6121 test(ios-prefetch): enhance testing infrastructure and validation
Apply comprehensive enhancements to iOS prefetch plugin testing and
validation system per directive requirements.

Technical Correctness Improvements:
- Enhanced BGTask scheduling with validation (60s minimum lead time)
- Implemented one active task rule (cancel existing before scheduling)
- Added graceful simulator error handling (Code=1 expected)
- Follow Apple best practice: schedule next task immediately at execution
- Ensure task completion even on expiration with guard flag
- Improved error handling and structured logging

Testing Coverage Expansion:
- Added edge case scenarios table (7 scenarios: Background Refresh Off,
  Low Power Mode, Force-Quit, Timezone Change, DST, Multi-Day, Reboot)
- Expanded failure injection tests (8 new negative-path scenarios)
- Documented automated testing strategies (unit and integration tests)

Validation Enhancements:
- Added structured JSON logging schema for events
- Provided log validation script (validate-ios-logs.sh)
- Enhanced test run template with telemetry and state verification
- Documented state integrity checks (content hash, schedule hash)
- Added UI indicators and persistent test artifacts requirements

Documentation Updates:
- Enhanced IOS_PREFETCH_TESTING.md with comprehensive test strategies
- Added Technical Correctness Requirements to IOS_TEST_APP_REQUIREMENTS.md
- Expanded error handling test cases from 2 to 7 scenarios
- Created ENHANCEMENTS_APPLIED.md summary document

Files modified:
- ios/Plugin/DailyNotificationBackgroundTaskTestHarness.swift: Enhanced
  with technical correctness improvements
- doc/test-app-ios/IOS_PREFETCH_TESTING.md: Expanded testing coverage
- doc/test-app-ios/IOS_TEST_APP_REQUIREMENTS.md: Added technical
  requirements
- doc/test-app-ios/ENHANCEMENTS_APPLIED.md: New summary document
2025-11-17 06:37:06 +00:00

29 KiB
Raw Blame History

iOS Test App Requirements

Purpose: What the iOS test app must provide so that the testing guide can be executed with parity vs Android

Version: 1.0.1
Scope: Phase 1 Prefetch MVP
Next Target: Phase 2 (Rolling Window + TTL Telemetry)
Maintainer: Matthew Raymer
Status: 📋 REQUIRED FOR PHASE 1
Date: 2025-11-15
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 doc/test-app-ios/IOS_PREFETCH_GLOSSARY.md for complete terminology definitions.


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:

  1. Copy HTML/JS from Android test app (test-apps/android-test-app/app/src/main/assets/public/index.html)
  2. Wire plugin into Capacitor (capacitor.config.json)
  3. Add Info.plist keys (BGTask identifiers, background modes, notification permissions)
  4. Build/run (./scripts/build-ios-test-app.sh --simulator or Xcode)
  5. Press buttons: Check Plugin Status → Request Permissions → Schedule Test Notification
  6. 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:

  1. Plugin Status Check

    • Display plugin availability status
    • Show "Plugin is loaded and ready!" when available
  2. Permission Management

    • Display current permission status
    • Request permissions button
    • Check permissions button
    • Show / indicators for each permission
  3. 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
  4. Notification Testing

    • Schedule test notification button
    • Display scheduled time
    • Show notification status
  5. 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

  1. Open Workspace:

    cd test-apps/ios-test-app
    open App.xcworkspace  # or App.xcodeproj
    
  2. Select Target:

    • Choose iOS Simulator (iPhone 15, iPhone 15 Pro, etc.)
    • Or physical device (requires signing)
  3. 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

Phase 2 Enhancement: Refactor into modular subcommands:

./scripts/build-ios-test-app.sh setup   # pod install + sync
./scripts/build-ios-test-app.sh run-sim # build + run simulator
./scripts/build-ios-test-app.sh device  # build + deploy device

Copy-Paste Commands:

# Setup (first time or after dependency changes)
cd test-apps/ios-test-app
pod install
npx cap sync ios

# Build for simulator
xcodebuild -workspace App.xcworkspace \
  -scheme ios-test-app \
  -configuration Debug \
  -sdk iphonesimulator \
  -destination 'platform=iOS Simulator,name=iPhone 15' \
  build

# Run on simulator
xcodebuild -workspace App.xcworkspace \
  -scheme ios-test-app \
  -configuration Debug \
  -sdk iphonesimulator \
  -destination 'platform=iOS Simulator,name=iPhone 15' \
  test

Future CI Integration (Optional)

Note for Phase 2+: Consider adding xcodebuild-based CI job that:

  • Builds test-apps/ios-test-app for simulator
  • Runs a minimal UI test that:
    • Launches app
    • Calls configure() and getNotificationStatus()
  • Validates log sequence + successful fetch simulation via LLDB trigger
  • This ensures test app remains buildable as plugin evolves

Phase 1: Manual testing only; CI integration is out of scope.

CI Readiness (Phase 2):

  • Add xcodebuild target for "Prefetch Integration Test"
  • Validate log sequence + successful fetch simulation via LLDB trigger
  • Use log validation script (validate-ios-logs.sh) for automated sequence checking

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 install before first build)
  • Node.js: 20.x (recommended)
  • npm: Latest stable (comes with Node.js)
  • Xcode Command Line Tools: Must run xcode-select --install if 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"]

Copy-Paste Commands:

// In Xcode LLDB console:
// 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"]

// Force reschedule all tasks (test harness)
po DailyNotificationBackgroundTaskTestHarness.forceRescheduleAll()

// Simulate time warp (test harness)
po DailyNotificationBackgroundTaskTestHarness.simulateTimeWarp(minutesForward: 60)

Console.app Logging

  1. Open Console.app (Applications → Utilities)
  2. Select device/simulator
  3. Filter by: DNP- or DailyNotification

Key Log Prefixes:

  • DNP-PLUGIN: - Main plugin operations
  • DNP-FETCH: - Background fetch operations
  • DNP-SCHEDULER: - Scheduling operations
  • DNP-STORAGE: - Storage operations

Structured Logging (Swift Logger):

The plugin uses Swift Logger categories for structured logging:

  • com.timesafari.dailynotification.plugin - Plugin operations
  • com.timesafari.dailynotification.fetch - Fetch operations
  • com.timesafari.dailynotification.scheduler - Scheduling operations
  • com.timesafari.dailynotification.storage - Storage operations

Filter in Console.app by subsystem: com.timesafari.dailynotification

Phase 2: Log Validation Script

Add helper script (validate-ios-logs.sh) to grep for required sequence markers:

grep -E "\[DNP-(FETCH|SCHEDULER|PLUGIN)\]" device.log | ./scripts/validate-ios-logs.sh

This confirms that all critical log steps occurred in proper order and flags missing or out-of-order events automatically.

Common Debugging Scenarios

Scenario: BGTask not running when expected → follow this checklist:

  1. 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
  2. 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
  3. 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 Limitations for details
    • Testing: Use Xcode → Debug → Simulate Background Fetch for simulator testing
  4. Plugin Not Discovered:

    • Check plugin class conforms to CAPBridgedPlugin protocol (required for Capacitor discovery)
    • Verify @objc extension DailyNotificationPlugin: CAPBridgedPlugin exists in plugin code
    • Check plugin framework is force-loaded in AppDelegate before Capacitor initializes
    • Verify pluginMethods array includes all @objc methods
    • See also: doc/directives/0003-iOS-Android-Parity-Directive.md Plugin Discovery Issue for detailed troubleshooting
  5. Build Failures:

    • Run pod install
    • Clean build folder (Cmd+Shift+K)
    • Verify Capacitor plugin path

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.

  1. 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
  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)
  3. 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
  1. 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
  2. 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

  1. Permission Denied [P1-Core][SIM+DEV]

    • Deny notification permissions
    • Try to schedule notification
    • Verify error returned
    • See also: IOS_PREFETCH_TESTING.md Simulator Test Plan: Step 7 (Negative-Path Tests)
  2. Invalid Parameters [P1-Core][SIM+DEV]

    • Try to schedule with invalid time format
    • Verify error returned
  3. Network Failures [P1-Prefetch][SIM+DEV]

    • Turn off network during fetch
    • Verify graceful fallback to on-demand fetch
    • Test recovery: network off → fail → network on → retry succeeds
  4. Server Errors / Auth Expiry [P1-Prefetch][SIM+DEV]

    • Simulate HTTP 401 or 500 error
    • Verify plugin logs failure with reason (AUTH, NETWORK)
    • Confirm telemetry counter increments: dnp_prefetch_failure_total{reason="AUTH"}
    • Verify fallback to live fetch at notification time
  5. Corrupted Cache [P1-Prefetch][SIM+DEV]

    • Manually tamper with stored cache (invalid JSON or remove entry)
    • Verify plugin detects invalid cache and falls back
    • Ensure no crash on reading bad cache (error handling wraps cache read)
  6. BGTask Execution Failure [P1-Prefetch][SIM+DEV]

    • Simulate internal failure in BGTask handler
    • Verify expiration handler or completion still gets called
    • Ensure task marked complete even on exception
  7. Repeated Scheduling Calls [P1-Prefetch][SIM+DEV]

    • Call scheduleDailyNotification() multiple times rapidly
    • Verify no duplicate scheduling (one active task rule enforced)
    • Confirm only one BGTask is actually submitted

Advanced Features [P2-Advanced]

  1. Rolling Window [P2-Advanced]

    • Schedule multiple notifications
    • Verify rolling window maintenance
    • Check notification limits (64 max on iOS)
  2. TTL Enforcement [P2-Advanced]

    • Schedule notification with prefetch
    • Wait for TTL to expire
    • Verify stale content discarded at delivery

Since this test app is for internal testing, it can expose more tools:

Dev-Only Toggles

Button Action Purpose
"Simulate BGTask Now" Calls LLDB trigger Quick sanity test
"Schedule 1-min Notification" Auto schedules T-Lead=1 Edge case testing
"Simulate DST Shift" Adds +1 hr offset DST handling check
"Show Cached Payload" Displays JSON cache Prefetch validation
"Force Reschedule All" Calls forceRescheduleAll() BGTask recovery testing
"Time Warp +N Minutes" Calls simulateTimeWarp() Accelerated TTL/T-Lead tests
  1. Force Schedule Notification N Minutes from Now

    • Bypass normal scheduling UI
    • Directly call scheduleDailyNotification() with calculated time
    • Useful for quick testing scenarios
  2. 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)
  3. Display Raw API Response

    • Show last fetched payload as JSON
    • Useful for debugging API responses
    • Can be referenced from IOS_PREFETCH_TESTING.md as shortcuts for specific scenarios
  4. Manual BGTask Trigger (Dev Build Only)

    • Button to manually trigger BGTask (simulator only)
    • Wraps the LLDB command in UI for convenience
  5. UI Feedback Enhancements

    • Add persistent toast/log area showing step-by-step plugin state ("Registered", "Scheduled", "BGTask fired", etc.)
    • Include color-coded state indicators for each stage (🟢 OK, 🟡 Pending, 🔴 Error)

These features can then be referenced from IOS_PREFETCH_TESTING.md as shortcuts for specific test scenarios.

Persistent Schedule Snapshot

Phase 2 Enhancement: Store a simple JSON of the last prefetch state for post-run verification:

{
  "last_schedule": "2025-11-15T05:48:00Z",
  "last_prefetch": "2025-11-15T05:50:00Z",
  "last_notification": "2025-11-15T05:53:00Z",
  "prefetch_success": true,
  "cached_content_used": true,
  "contentHash": "abcdef123456",
  "scheduleHash": "xyz789"
}

This can be used for post-run verification and telemetry aggregation. Access via test app UI button "Show Schedule Snapshot" or via UserDefaults key DNP_ScheduleSnapshot.

UI Indicators for Tests

Status Display:

  • Status label/icon: 🟢 green when last notification was fetched and delivered from cache
  • 🔴 red if something failed (network error, etc.)
  • 🟡 yellow if pending/unknown state

Last Operation Summary:

  • Display last fetch time
  • Show whether cached content was used
  • Display any error message
  • Show telemetry counter values

Dump Prefetch Status Button:

  • Triggers plugin to report all relevant info
  • Shows pending tasks, cache status, telemetry snapshot
  • Displays in scrollable text view
  • Useful on devices where attaching debugger is inconvenient

In-App Log Viewer (Phase 2)

For QA Use:

  • Read app's unified logging (OSLog) for entries with subsystem com.timesafari.dailynotification
  • Present logs on screen or allow export to file
  • Capture Logger output into text buffer during app session
  • Security: Only enable in test builds, not production

Export Test Results:

  • Save test run summary to file (JSON format)
  • Include timestamps, outcomes, telemetry counters
  • Access via "Export Test Results" button
  • Collect from devices via Xcode or CI pipelines

Risks & Gotchas

Common pitfalls when working with the test app:

  1. 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
  2. Forgetting npx cap sync ios after plugin or asset changes

    • Run npx cap sync ios after any plugin code changes
    • Run npx cap sync ios after any web asset changes
    • Check capacitor.config.json is up to date
  3. Running with stale Pods

    • Run pod repo update periodically
    • Run pod install after dependency changes
    • Clean build folder (Cmd+Shift+K) if Pods seem stale
  4. Changing BGTask identifiers in one place but not the other

    • Info.plist BGTaskSchedulerPermittedIdentifiers must match Swift code exactly (case-sensitive)
    • Check both places when updating identifiers
    • See IOS_PREFETCH_TESTING.md for identifier requirements
  5. 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)

Technical Correctness Requirements

BGTask Scheduling & Lifecycle

Validation Requirements:

  • Verify earliestBeginDate is at least 60 seconds in future (iOS requirement)
  • Log and handle scheduling errors gracefully (Code=1 on simulator is expected)
  • Cancel existing pending task before scheduling new (one active task rule)
  • Use BGTaskScheduler.shared.getPendingTaskRequests() in debug to verify only one task pending

Schedule Next Task at Execution:

  • Adopt Apple's best practice: schedule next task IMMEDIATELY at start of BGTask handler
  • This ensures continuity even if app is terminated shortly after
  • Pattern: Schedule next → Initiate async work → Mark complete → Use expiration handler

Expiration Handler and Completion:

  • Implement expiration handler to cancel ongoing operations if iOS terminates task (~30 seconds)
  • Always call task.setTaskCompleted(success:) exactly once
  • Use success: false if fetch didn't complete (system may reschedule sooner)
  • Use success: true if all went well
  • Re-schedule next task after marking completion (for recurring use cases)

Error Handling & Retry:

  • Distinguish recoverable errors (transient network) vs permanent failures
  • For network failures: log failure reason, set success: false, consider cached data if available
  • For logic errors: log clear message, call setTaskCompleted(success: false), exit cleanly
  • Ensure fallback to on-demand fetch at notification time if prefetch fails

Data Consistency & Cleanup:

  • Cross-check notificationTime matches payload's scheduled_for field
  • Validate TTL on cached content at notification time (discard if expired)
  • Ensure content is saved to persistent store before marking BGTask complete
  • Implement cache cleanup for outdated data
  • Handle permission changes gracefully (detect failure at delivery, log outcome)

Scheduling and Notification Coordination

Unified Scheduling Logic:

  • Atomically schedule both UNNotificationRequest and BGAppRefreshTaskRequest
  • If one fails, cancel the other or report partial failure
  • Return clear status/error code to JS layer

BGTask Identifier Constants:

  • Verify identifier in code exactly matches Info.plist (case-sensitive)
  • Test harness should verify on app launch (logs show successful registration)

Concurrency Considerations:

  • Handle potentially overlapping schedules (Phase 2: multiple notifications)
  • Use one BGTask to fetch for next upcoming notification only
  • Store next notification's schedule ID and time in shared place
  • Use locks or dispatch synchronization to avoid race conditions

OS Limits:

  • Acknowledge force-quit prevents BGTask execution (can't circumvent)
  • Tolerate running slightly later than earliestBeginDate (iOS heuristics)
  • Log actual execution time vs scheduled time for analysis

Phase 2 Forward Plan

Planned enhancements for Phase 2:

  • Add quick scenario buttons (Simulate BGTask Now, Schedule 1-min Notification, Simulate DST Shift, Show Cached Payload)
  • Implement persistent schedule snapshot (JSON of last prefetch state)
  • Add color-coded UI feedback (🟢 OK, 🟡 Pending, 🔴 Error)
  • Refactor build script into modular subcommands (setup, run-sim, device)
  • Integrate CI pipeline with xcodebuild target for "Prefetch Integration Test"
  • Add log validation script (validate-ios-logs.sh) for automated sequence checking
  • Implement rolling window & TTL validation
  • Add telemetry verification for multi-day scenarios
  • Test on different device models and iOS versions
  • Add in-app log viewer/export for QA use

See also: doc/directives/0003-iOS-Android-Parity-Directive.md for Phase 2 implementation details.


Changelog (high-level)

  • 2025-11-15 — Initial Phase 1 version (prefetch MVP, Android parity)

Status: 📋 REQUIRED FOR PHASE 1
Last Updated: 2025-11-15