docs(ios): add comprehensive testing guide and refine iOS parity directive
Add iOS prefetch testing guide with detailed procedures, log checklists, and behavior classification. Enhance iOS test app requirements with security constraints, sign-off checklists, and changelog structure. Update main directive with testing strategy and method behavior mapping. Changes: - Add IOS_PREFETCH_TESTING.md with simulator/device test plans, log diagnostics, telemetry expectations, and test run templates - Add DailyNotificationBackgroundTaskTestHarness.swift as reference implementation for BGTaskScheduler testing - Enhance IOS_TEST_APP_REQUIREMENTS.md with security/privacy constraints, review checklists, CI hints, and glossary cross-links - Update 0003-iOS-Android-Parity-Directive.md with testing strategy section, method behavior classification, and validation matrix updates All documents now include changelog stubs, cross-references, and completion criteria for Phase 1 implementation and testing.
This commit is contained in:
218
ios/Plugin/DailyNotificationBackgroundTaskTestHarness.swift
Normal file
218
ios/Plugin/DailyNotificationBackgroundTaskTestHarness.swift
Normal file
@@ -0,0 +1,218 @@
|
||||
//
|
||||
// DailyNotificationBackgroundTaskTestHarness.swift
|
||||
// DailyNotificationPlugin
|
||||
//
|
||||
// Test harness for BGTaskScheduler prefetch functionality
|
||||
// Reference implementation demonstrating task registration, scheduling, and handling
|
||||
//
|
||||
// See: doc/test-app-ios/IOS_PREFETCH_TESTING.md for testing procedures
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import BackgroundTasks
|
||||
import UIKit
|
||||
|
||||
/// Minimal BGTaskScheduler test harness for DailyNotificationPlugin prefetch testing
|
||||
///
|
||||
/// This is a reference implementation demonstrating:
|
||||
/// - Task registration
|
||||
/// - Task scheduling
|
||||
/// - Task handler implementation
|
||||
/// - Expiration handling
|
||||
/// - Completion reporting
|
||||
///
|
||||
/// **Usage:**
|
||||
/// - Reference this when implementing actual prefetch logic in `DailyNotificationBackgroundTaskManager.swift`
|
||||
/// - Use in test app for debugging BGTaskScheduler behavior
|
||||
/// - See `doc/test-app-ios/IOS_PREFETCH_TESTING.md` for comprehensive testing guide
|
||||
///
|
||||
/// **Info.plist Requirements:**
|
||||
/// ```xml
|
||||
/// <key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
/// <array>
|
||||
/// <string>com.timesafari.dailynotification.fetch</string>
|
||||
/// </array>
|
||||
/// ```
|
||||
///
|
||||
/// **Background Modes (Xcode Capabilities):**
|
||||
/// - Background fetch
|
||||
/// - Background processing (if using BGProcessingTask)
|
||||
class DailyNotificationBackgroundTaskTestHarness {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
static let prefetchTaskIdentifier = "com.timesafari.dailynotification.fetch"
|
||||
|
||||
// MARK: - Registration
|
||||
|
||||
/// Register BGTaskScheduler task handler
|
||||
///
|
||||
/// Call this in AppDelegate.application(_:didFinishLaunchingWithOptions:)
|
||||
/// before app finishes launching.
|
||||
static func registerBackgroundTasks() {
|
||||
BGTaskScheduler.shared.register(
|
||||
forTaskWithIdentifier: prefetchTaskIdentifier,
|
||||
using: nil
|
||||
) { task in
|
||||
// This closure is called when the task is launched by the system
|
||||
handlePrefetchTask(task: task as! BGAppRefreshTask)
|
||||
}
|
||||
|
||||
print("[DNP-FETCH] Registered BGTaskScheduler with id=\(prefetchTaskIdentifier)")
|
||||
}
|
||||
|
||||
// MARK: - Scheduling
|
||||
|
||||
/// Schedule a BGAppRefreshTask for prefetch
|
||||
///
|
||||
/// - Parameter earliestOffsetSeconds: Seconds from now when task can begin
|
||||
/// - Returns: true if scheduling succeeded, false otherwise
|
||||
@discardableResult
|
||||
static func schedulePrefetchTask(earliestOffsetSeconds: TimeInterval) -> Bool {
|
||||
let request = BGAppRefreshTaskRequest(identifier: prefetchTaskIdentifier)
|
||||
request.earliestBeginDate = Date(timeIntervalSinceNow: earliestOffsetSeconds)
|
||||
|
||||
do {
|
||||
try BGTaskScheduler.shared.submit(request)
|
||||
print("[DNP-FETCH] BGAppRefreshTask scheduled (earliestBeginDate=\(String(describing: request.earliestBeginDate)))")
|
||||
return true
|
||||
} catch {
|
||||
print("[DNP-FETCH] Failed to schedule BGAppRefreshTask: \(error)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Handler
|
||||
|
||||
/// Handle BGAppRefreshTask execution
|
||||
///
|
||||
/// This is called by the system when the background task is launched.
|
||||
/// Replace PrefetchOperation with your actual prefetch logic.
|
||||
private static func handlePrefetchTask(task: BGAppRefreshTask) {
|
||||
print("[DNP-FETCH] BGTask handler invoked (task.identifier=\(task.identifier))")
|
||||
|
||||
// Schedule the next one early, so that there's always a pending task
|
||||
// In real implementation, calculate next schedule based on notification time
|
||||
schedulePrefetchTask(earliestOffsetSeconds: 60 * 30) // 30 minutes later, for example
|
||||
|
||||
// Define the work
|
||||
let queue = OperationQueue()
|
||||
queue.maxConcurrentOperationCount = 1
|
||||
|
||||
let operation = PrefetchOperation()
|
||||
|
||||
// Set expiration handler
|
||||
// Called if iOS decides to end the task early (typically ~30 seconds)
|
||||
task.expirationHandler = {
|
||||
print("[DNP-FETCH] Task expired")
|
||||
operation.cancel()
|
||||
}
|
||||
|
||||
// Set completion handler
|
||||
operation.completionBlock = {
|
||||
let success = !operation.isCancelled
|
||||
print("[DNP-FETCH] Task completionBlock (success=\(success))")
|
||||
task.setTaskCompleted(success: success)
|
||||
}
|
||||
|
||||
queue.addOperation(operation)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Prefetch Operation
|
||||
|
||||
/// Simple Operation example for testing
|
||||
///
|
||||
/// Replace this with your actual prefetch logic:
|
||||
/// - HTTP fetch from TimeSafari API
|
||||
/// - JWT signing
|
||||
/// - ETag validation
|
||||
/// - Content caching
|
||||
/// - Error handling
|
||||
class PrefetchOperation: Operation {
|
||||
|
||||
override func main() {
|
||||
if isCancelled { return }
|
||||
|
||||
print("[DNP-FETCH] PrefetchOperation: starting fake fetch...")
|
||||
|
||||
// Simulate some work
|
||||
// In real implementation, this would be:
|
||||
// - Make HTTP request
|
||||
// - Parse response
|
||||
// - Cache content
|
||||
// - Update database
|
||||
Thread.sleep(forTimeInterval: 2)
|
||||
|
||||
if isCancelled { return }
|
||||
|
||||
print("[DNP-FETCH] PrefetchOperation: finished fake fetch.")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AppDelegate Integration Example
|
||||
|
||||
/*
|
||||
Example integration in AppDelegate.swift:
|
||||
|
||||
import UIKit
|
||||
import BackgroundTasks
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
|
||||
// Register background tasks BEFORE app finishes launching
|
||||
DailyNotificationBackgroundTaskTestHarness.registerBackgroundTasks()
|
||||
|
||||
// Schedule initial task (for testing)
|
||||
DailyNotificationBackgroundTaskTestHarness.schedulePrefetchTask(earliestOffsetSeconds: 5 * 60) // 5 minutes
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// MARK: - Testing in Simulator
|
||||
|
||||
/*
|
||||
To test in simulator:
|
||||
|
||||
1. Run app in Xcode
|
||||
2. Background the app (Home button / Cmd+Shift+H)
|
||||
3. In Xcode menu:
|
||||
- Debug → Simulate Background Fetch, or
|
||||
- Debug → Simulate Background Refresh
|
||||
4. Check console logs for [DNP-FETCH] messages
|
||||
|
||||
Expected logs:
|
||||
- [DNP-FETCH] Registered BGTaskScheduler with id=...
|
||||
- [DNP-FETCH] BGAppRefreshTask scheduled (earliestBeginDate=...)
|
||||
- [DNP-FETCH] BGTask handler invoked (task.identifier=...)
|
||||
- [DNP-FETCH] PrefetchOperation: starting fake fetch...
|
||||
- [DNP-FETCH] PrefetchOperation: finished fake fetch.
|
||||
- [DNP-FETCH] Task completionBlock (success=true)
|
||||
*/
|
||||
|
||||
// MARK: - Testing on Real Device
|
||||
|
||||
/*
|
||||
To test on real device:
|
||||
|
||||
1. Install app on iPhone
|
||||
2. Enable Background App Refresh in Settings → [Your App]
|
||||
3. Schedule a notification with prefetch
|
||||
4. Lock device and leave idle (plugged in for best results)
|
||||
5. Monitor logs via:
|
||||
- Xcode → Devices & Simulators → device → open console
|
||||
- Or os_log aggregator / remote logging
|
||||
|
||||
Note: Real device timing is heuristic, not deterministic.
|
||||
iOS will run the task when it determines it's appropriate,
|
||||
not necessarily at the exact earliestBeginDate.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user