docs(ios): add comprehensive iOS implementation documentation

Adds complete iOS documentation suite to support iOS implementation
parity with Android features. Includes implementation directives,
recovery scenario mappings, database migration guide, troubleshooting
guide, and test scripts.

New Documentation:
- iOS Implementation Directive: Phase-based implementation guide
  mirroring Android structure with iOS-specific considerations
- iOS Recovery Scenario Mapping: Maps Android recovery scenarios
  to iOS equivalents with detection logic comparisons
- iOS Core Data Migration Guide: Complete Room → Core Data entity
  mappings with implementation checklist for missing entities
- iOS Troubleshooting Guide: Common issues, debugging techniques,
  and error code reference

Enhanced Documentation:
- API.md: Added iOS-only methods (permissions, background tasks,
  pending notifications), platform differences table, and iOS-specific
  error types. Updated version to 2.3.0.

Test Infrastructure:
- iOS test scripts for Phase 1 (cold start), Phase 2 (termination),
  and Phase 3 (boot recovery) testing scenarios

All documentation addresses gaps identified in iOS Implementation
Documentation Review and provides foundation for iOS recovery feature
implementation (currently pending).

Note: iOS recovery features (ReactivationManager, scenario detection)
are NOT yet implemented. Documentation is ready to guide implementation.
This commit is contained in:
Matthew
2025-12-08 23:36:30 -08:00
parent dac9cf3ddc
commit dd8d67462f
9 changed files with 2805 additions and 6 deletions

View File

@@ -0,0 +1,436 @@
#!/bin/bash
# Phase 1 Testing Script - iOS Interactive Test Runner
# Guides through all Phase 1 tests with clear prompts for UI interaction
# Adapted from Android test-phase1.sh for iOS testing
set -e # Exit on error
# Source shared library (if exists)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "${SCRIPT_DIR}/ios-test-lib.sh" ]; then
source "${SCRIPT_DIR}/ios-test-lib.sh"
fi
# Phase 1 specific configuration
APP_BUNDLE_ID="com.timesafari.ios-test-app"
SIMULATOR_DEVICE="iPhone 15"
LOG_PREFIX="DNP"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Helper functions
print_header() {
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
}
print_step() {
echo -e "${GREEN}[STEP $1]${NC} $2"
echo ""
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warn() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
wait_for_user() {
echo ""
read -p "Press Enter to continue..."
echo ""
}
wait_for_ui_action() {
echo ""
echo -e "${YELLOW}$1${NC}"
echo ""
read -p "Press Enter when done..."
echo ""
}
# iOS-specific helper functions
get_simulator_id() {
xcrun simctl list devices available | grep "${SIMULATOR_DEVICE}" | head -1 | sed -E 's/.*\(([^)]+)\).*/\1/'
}
get_app_logs() {
local device_id=$1
local lines=${2:-50}
xcrun simctl spawn "${device_id}" log stream --level=debug --predicate 'processImagePath contains "ios-test-app"' --style=compact 2>/dev/null | head -n "${lines}" || echo ""
}
check_plugin_configured() {
print_info "Checking if plugin is configured..."
local device_id=$(get_simulator_id)
if [ -z "${device_id}" ]; then
print_error "Simulator not found: ${SIMULATOR_DEVICE}"
return 1
fi
# Check if app has been launched (indicates configuration may exist)
local app_data=$(xcrun simctl get_app_container "${device_id}" "${APP_BUNDLE_ID}" 2>/dev/null || echo "")
if [ -n "${app_data}" ]; then
print_success "App data exists (plugin may be configured)"
print_info "Please verify in the app UI that you see:"
echo " ⚙️ Plugin Settings: ✅ Configured"
echo " 🔌 Native Fetcher: ✅ Configured"
echo ""
echo "If both show ✅, the plugin is configured and you can skip configuration."
return 0
else
print_info "Plugin not configured (no app data found)"
print_info "You will need to click 'Configure Plugin' in the app UI"
return 1
fi
}
check_permissions() {
print_info "Checking notification permissions..."
# Note: iOS permissions are checked at runtime, not via command line
# We can only check if the app has been granted permission by checking logs
print_info "iOS notification permissions are checked at runtime."
print_info "Please verify in the app UI that notifications are authorized."
print_info "If not authorized, you'll need to grant permission in the app."
return 0
}
ensure_permissions() {
if check_permissions; then
print_success "Permissions check passed"
return 0
else
print_info "Notification permissions needed"
wait_for_ui_action "In the app UI, click the 'Request Permissions' button.
This will show a system permission dialog.
Steps:
1. Click 'Request Permissions' button
2. In the system dialog, tap 'Allow' to grant notification permission
3. Return to the app and verify the status shows:
- 🔔 Notifications: ✅ Granted (or similar)
Once permission is granted, press Enter to continue."
return 0
fi
}
launch_app() {
print_info "Launching app on simulator..."
local device_id=$(get_simulator_id)
if [ -z "${device_id}" ]; then
print_error "Simulator not found: ${SIMULATOR_DEVICE}"
print_info "Available simulators:"
xcrun simctl list devices available | grep "iPhone" | head -5
return 1
fi
# Boot simulator if not running
local booted=$(xcrun simctl list devices | grep "${device_id}" | grep -c "Booted" || echo "0")
if [ "${booted}" -eq "0" ]; then
print_info "Booting simulator..."
xcrun simctl boot "${device_id}" 2>/dev/null || true
sleep 3
fi
# Launch app
xcrun simctl launch "${device_id}" "${APP_BUNDLE_ID}" 2>/dev/null || {
print_warn "App may already be running or needs to be built first"
print_info "Please build and run the app in Xcode first:"
echo " 1. Open: test-apps/ios-test-app/ios/App/App.xcworkspace"
echo " 2. Select simulator: ${SIMULATOR_DEVICE}"
echo " 3. Press Cmd+R to build and run"
echo ""
wait_for_user
}
sleep 2 # Give app time to launch
print_success "App launched"
}
get_pending_notifications() {
local device_id=$(get_simulator_id)
if [ -z "${device_id}" ]; then
echo "0"
return
fi
# Note: iOS doesn't provide direct command-line access to pending notifications
# This would need to be implemented via a plugin method
# For now, we'll check logs for notification scheduling
local count=$(get_app_logs "${device_id}" 100 | grep -c "scheduled notification" || echo "0")
echo "${count}"
}
should_run_test() {
local test_id=$1
shift
local selected_tests=("$@")
if [ ${#selected_tests[@]} -eq 0 ]; then
return 0 # Run all tests if none specified
fi
for selected in "${selected_tests[@]}"; do
if [ "${selected}" = "${test_id}" ]; then
return 0
fi
done
return 1
}
# ============================================
# TEST 0: Daily Rollover (Core Contract Verification)
# ============================================
if should_run_test "0" "$@"; then
print_header "TEST 0: Daily Rollover Verification"
echo "Purpose: Verify that after a notification fires, the next day's"
echo " schedule is correctly computed and only ONE notification exists."
echo ""
echo "Note: This test verifies the core 'one notification per day' contract."
echo " It requires either:"
echo " 1. Scheduling a notification for 'now + N seconds' and waiting, OR"
echo " 2. Manipulating the simulator clock to cross the fire boundary."
echo ""
wait_for_user
print_step "1" "Schedule a test notification for near-future..."
launch_app
check_plugin_configured || {
wait_for_ui_action "In the app UI, click 'Configure Plugin' to set up the plugin.
This will initialize the database and storage.
Once configured, press Enter to continue."
}
INITIAL_COUNT=$(get_pending_notifications)
print_info "Current pending notifications: ${INITIAL_COUNT}"
wait_for_ui_action "In the app UI, schedule a daily notification.
For this test, you may want to schedule it for a time very soon
(e.g., 1-2 minutes from now) to observe the rollover behavior.
This will schedule:
- 1 notification (UNUserNotificationCenter) for the specified time
- 1 prefetch task (BGTaskScheduler) for 2 minutes before that time"
sleep 3 # Give notification time to be registered
POST_SCHEDULE_COUNT=$(get_pending_notifications)
print_info "Pending notifications after scheduling: ${POST_SCHEDULE_COUNT}"
print_step "2" "Manual verification steps..."
echo ""
echo "To complete this test, you need to:"
echo " 1. Wait for the notification to fire (or advance simulator clock)"
echo " 2. Check that the plugin:"
echo " - Computed the next day's time (24 hours later)"
echo " - Scheduled exactly ONE notification for tomorrow"
echo " - Did NOT create duplicate notifications"
echo " 3. Verify in logs (Xcode Console or Console.app):"
echo " - Next run time calculation shows tomorrow's time"
echo " - Only one notification scheduled"
echo ""
echo "Expected log patterns:"
echo " DNP-SCHEDULE: Scheduling next daily notification: ... source=ROLLOVER_ON_FIRE"
echo " DNP-NOTIFY: Scheduling notification: triggerTime=<tomorrow's time>"
echo ""
wait_for_ui_action "After the notification fires (or you advance the clock),
check the logs and verify:
1. Only ONE notification exists (one per day)
2. The notification time is for tomorrow (24 hours later)
3. No duplicate notifications were created
Press Enter when verification is complete."
print_success "TEST 0: Daily Rollover Verification - Manual verification required"
fi
# ============================================
# TEST 1: Cold Start Recovery
# ============================================
if should_run_test "1" "$@"; then
print_header "TEST 1: Cold Start Recovery"
echo "Purpose: Verify that when the app launches after termination,"
echo " missed notifications are detected and future notifications"
echo " are verified/rescheduled."
echo ""
wait_for_user
print_step "1" "Schedule a notification for future time..."
launch_app
ensure_permissions
wait_for_ui_action "In the app UI, schedule a daily notification for a future time
(e.g., 1 hour from now).
This creates a notification that should persist across app termination."
sleep 2
print_step "2" "Terminate app (simulate cold start)..."
print_info "Terminating app to simulate cold start scenario..."
local device_id=$(get_simulator_id)
xcrun simctl terminate "${device_id}" "${APP_BUNDLE_ID}" 2>/dev/null || true
print_info "App terminated. Waiting 5 seconds..."
sleep 5
print_step "3" "Launch app and verify recovery..."
launch_app
print_info "Checking logs for recovery activity..."
sleep 3
local device_id=$(get_simulator_id)
local logs=$(get_app_logs "${device_id}" 100)
if echo "${logs}" | grep -q "DNP-REACTIVATION\|recovery\|missed"; then
print_success "Recovery activity detected in logs"
else
print_warn "No recovery activity detected in logs"
print_info "This may indicate recovery is not yet implemented (expected for Phase 1)"
fi
wait_for_ui_action "Verify in the app UI that:
1. The notification is still scheduled (check 'Scheduled Notifications' screen)
2. Any missed notifications are marked as missed
3. Future notifications are verified/rescheduled
Press Enter when verification is complete."
print_success "TEST 1: Cold Start Recovery - Manual verification required"
fi
# ============================================
# TEST 2: Notification Persistence (Swipe from App Switcher)
# ============================================
if should_run_test "2" "$@"; then
print_header "TEST 2: Notification Persistence (App Termination)"
echo "Purpose: Verify that notifications persist when app is terminated"
echo " (iOS OS-guaranteed behavior)."
echo ""
wait_for_user
print_step "1" "Schedule a notification..."
launch_app
ensure_permissions
wait_for_ui_action "In the app UI, schedule a daily notification for a future time."
sleep 2
print_step "2" "Terminate app..."
print_info "Terminating app (simulating swipe from app switcher)..."
local device_id=$(get_simulator_id)
xcrun simctl terminate "${device_id}" "${APP_BUNDLE_ID}" 2>/dev/null || true
print_info "App terminated. Waiting 3 seconds..."
sleep 3
print_step "3" "Verify notification still exists..."
print_info "On iOS, notifications persist automatically (OS-guaranteed)."
print_info "The notification should still fire even though the app is terminated."
wait_for_ui_action "Verify that:
1. The notification fires at the scheduled time (even though app is terminated)
2. When you tap the notification, the app launches
3. The notification is marked as delivered
Press Enter when verification is complete."
print_success "TEST 2: Notification Persistence - iOS OS-guaranteed behavior verified"
fi
# ============================================
# TEST 3: Invalid Data Handling
# ============================================
if should_run_test "3" "$@"; then
print_header "TEST 3: Invalid Data Handling"
echo "Purpose: Verify that the plugin handles invalid data gracefully"
echo " without crashing."
echo ""
wait_for_user
print_step "1" "Test invalid notification time..."
launch_app
ensure_permissions
wait_for_ui_action "In the app UI, try to schedule a notification with invalid data:
1. Empty time string
2. Invalid time format (e.g., '25:00' or '12:99')
3. Negative time values
The app should show an error message and NOT crash."
print_info "Checking logs for error handling..."
sleep 2
local device_id=$(get_simulator_id)
local logs=$(get_app_logs "${device_id}" 50)
if echo "${logs}" | grep -q "error\|invalid\|Error"; then
print_success "Error handling detected in logs"
else
print_info "No errors in recent logs (may indicate graceful handling)"
fi
wait_for_ui_action "Verify that:
1. Invalid data is rejected with clear error messages
2. The app does NOT crash
3. Valid notifications can still be scheduled after errors
Press Enter when verification is complete."
print_success "TEST 3: Invalid Data Handling - Manual verification required"
fi
# ============================================
# Summary
# ============================================
print_header "Phase 1 Testing Complete"
echo "All Phase 1 tests have been executed."
echo ""
echo "Note: iOS recovery features (ReactivationManager) are NOT yet implemented."
echo " Tests 1 and 2 will show expected behavior once recovery is implemented."
echo ""
echo "Next Steps:"
echo " 1. Review test results"
echo " 2. Check logs for any errors"
echo " 3. Implement recovery features (Phase 1 directive)"
echo " 4. Re-run tests after implementation"
echo ""

View File

@@ -0,0 +1,58 @@
#!/bin/bash
# Phase 2 Testing Script - iOS App Termination Recovery
# Tests app termination detection and recovery (iOS equivalent of Android force stop)
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "${SCRIPT_DIR}/test-phase1.sh" ]; then
source "${SCRIPT_DIR}/test-phase1.sh" 2>/dev/null || true
fi
print_header "Phase 2: App Termination Recovery Testing"
echo "Note: iOS doesn't have user-facing 'force stop' like Android."
echo " This tests system termination scenarios and recovery."
echo ""
# Note: Phase 2 features are NOT yet implemented
print_warn "⚠️ Phase 2 recovery features (termination detection) are NOT yet implemented."
print_info "These tests will verify expected behavior once implementation is complete."
echo ""
wait_for_user
print_header "TEST 1: App Termination Detection"
echo "Purpose: Verify that when app is terminated by system,"
echo " recovery detects termination and reschedules notifications."
echo ""
launch_app
check_plugin_configured
wait_for_ui_action "Schedule a notification for future time."
print_info "Terminating app to simulate system termination..."
local device_id=$(get_simulator_id)
xcrun simctl terminate "${device_id}" "${APP_BUNDLE_ID}" 2>/dev/null || true
sleep 3
print_info "Launching app to trigger recovery..."
launch_app
sleep 5
print_info "Checking logs for termination detection..."
local device_id=$(get_simulator_id)
local logs=$(get_app_logs "${device_id}" 100)
if echo "${logs}" | grep -q "termination\|DNP-REACTIVATION"; then
print_success "Recovery activity detected"
else
print_warn "No recovery activity detected (expected - not yet implemented)"
fi
wait_for_ui_action "Verify notifications are rescheduled after termination."
print_success "Phase 2 testing complete (implementation pending)"

View File

@@ -0,0 +1,52 @@
#!/bin/bash
# Phase 3 Testing Script - iOS Boot Recovery
# Tests boot recovery and background task registration
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "${SCRIPT_DIR}/test-phase1.sh" ]; then
source "${SCRIPT_DIR}/test-phase1.sh" 2>/dev/null || true
fi
print_header "Phase 3: Boot Recovery Testing"
echo "Note: iOS automatically persists notifications across reboot (OS-guaranteed)."
echo " This tests BGTaskScheduler registration and boot recovery logic."
echo ""
# Note: Phase 3 features are NOT yet implemented
print_warn "⚠️ Phase 3 recovery features (BGTaskScheduler boot recovery) are NOT yet implemented."
print_info "These tests will verify expected behavior once implementation is complete."
echo ""
wait_for_user
print_header "TEST 1: Boot Recovery with Future Notifications"
echo "Purpose: Verify notifications persist across reboot and recovery logic runs."
echo ""
launch_app
check_plugin_configured
wait_for_ui_action "Schedule a notification for future time."
print_info "On iOS, notifications persist automatically across reboot."
print_info "We'll verify BGTaskScheduler registration and recovery logic."
print_warn "⚠️ Simulator reboot testing requires manual steps:"
echo " 1. Schedule notification"
echo " 2. Reboot simulator (Device → Restart in Simulator menu)"
echo " 3. Launch app after reboot"
echo " 4. Verify notifications still exist"
echo " 5. Check logs for recovery activity"
echo ""
wait_for_ui_action "After rebooting simulator and launching app,
verify that:
1. Notifications still exist (iOS OS-guaranteed)
2. Recovery logic runs (once implemented)
3. Any missed notifications are detected"
print_success "Phase 3 testing complete (implementation pending)"