Files
daily-notification-plugin/doc/test-app-ios/IOS_TEST_APP_REQUIREMENTS.md
Matthew d7a2dbb9fd docs(ios): update test app docs with recent implementation details
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
2025-11-16 21:53:56 -08:00

19 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

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:

  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

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()
  • 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 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"]

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

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]

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

    • Try to schedule with invalid time format
    • Verify error returned

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

  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

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:

  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)

Changelog (high-level)

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

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