Implement 4 of 8 Phase 2 iOS enhancements from TODO review. Changes: - DailyNotificationStateActor: Remove TODOs, implement TTL validation - maintainRollingWindow(): Already implemented, removed TODO - validateContentFreshness(): Now calls ttlEnforcer.validateBeforeArming() - DailyNotificationDatabase: Add queryInt() method for PRAGMA queries - Enables database statistics collection (page_count, page_size, cache_size) - DailyNotificationPerformanceOptimizer: Implement database stats and metrics - analyzeDatabasePerformance(): Queries PRAGMA values and records metrics - Removed 2 TODOs (database statistics, metrics recording) Verification: - TypeScript typecheck: PASS - All TODOs removed from fixed files Remaining Phase 2 items (4): - DailyNotificationBackgroundTasks: CoreData history - DailyNotificationReactivationManager: Fetcher instance - DailyNotificationPlugin: Fetcher instance - Additional items to verify
209 lines
5.2 KiB
Swift
209 lines
5.2 KiB
Swift
/**
|
|
* DailyNotificationStateActor.swift
|
|
*
|
|
* Actor for thread-safe state access
|
|
* Serializes all access to shared state (database, storage, rolling window, TTL enforcer)
|
|
*
|
|
* @author Matthew Raymer
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
import Foundation
|
|
|
|
/**
|
|
* Actor for thread-safe state access
|
|
*
|
|
* This actor serializes all access to:
|
|
* - DailyNotificationDatabase
|
|
* - DailyNotificationStorage
|
|
* - DailyNotificationRollingWindow
|
|
* - DailyNotificationTTLEnforcer
|
|
*
|
|
* All plugin methods and background tasks must access shared state through this actor.
|
|
*/
|
|
@available(iOS 13.0, *)
|
|
actor DailyNotificationStateActor {
|
|
|
|
// MARK: - Properties
|
|
|
|
private let database: DailyNotificationDatabase
|
|
private let storage: DailyNotificationStorage
|
|
private let rollingWindow: DailyNotificationRollingWindow?
|
|
private let ttlEnforcer: DailyNotificationTTLEnforcer?
|
|
|
|
// MARK: - Initialization
|
|
|
|
/**
|
|
* Initialize state actor with components
|
|
*
|
|
* @param database Database instance
|
|
* @param storage Storage instance
|
|
* @param rollingWindow Rolling window instance (optional, Phase 2)
|
|
* @param ttlEnforcer TTL enforcer instance (optional, Phase 2)
|
|
*/
|
|
init(
|
|
database: DailyNotificationDatabase,
|
|
storage: DailyNotificationStorage,
|
|
rollingWindow: DailyNotificationRollingWindow? = nil,
|
|
ttlEnforcer: DailyNotificationTTLEnforcer? = nil
|
|
) {
|
|
self.database = database
|
|
self.storage = storage
|
|
self.rollingWindow = rollingWindow
|
|
self.ttlEnforcer = ttlEnforcer
|
|
}
|
|
|
|
// MARK: - Storage Operations
|
|
|
|
/**
|
|
* Save notification content
|
|
*
|
|
* @param content Notification content to save
|
|
*/
|
|
func saveNotificationContent(_ content: NotificationContent) {
|
|
storage.saveNotificationContent(content)
|
|
}
|
|
|
|
/**
|
|
* Get notification content by ID
|
|
*
|
|
* @param id Notification ID
|
|
* @return Notification content or nil
|
|
*/
|
|
func getNotificationContent(id: String) -> NotificationContent? {
|
|
return storage.getNotificationContent(id: id)
|
|
}
|
|
|
|
/**
|
|
* Get last notification
|
|
*
|
|
* @return Last notification or nil
|
|
*/
|
|
func getLastNotification() -> NotificationContent? {
|
|
return storage.getLastNotification()
|
|
}
|
|
|
|
/**
|
|
* Get all notifications
|
|
*
|
|
* @return Array of all notifications
|
|
*/
|
|
func getAllNotifications() -> [NotificationContent] {
|
|
return storage.getAllNotifications()
|
|
}
|
|
|
|
/**
|
|
* Get ready notifications
|
|
*
|
|
* @return Array of ready notifications
|
|
*/
|
|
func getReadyNotifications() -> [NotificationContent] {
|
|
return storage.getReadyNotifications()
|
|
}
|
|
|
|
/**
|
|
* Delete notification content
|
|
*
|
|
* @param id Notification ID
|
|
*/
|
|
func deleteNotificationContent(id: String) {
|
|
storage.deleteNotificationContent(id: id)
|
|
}
|
|
|
|
/**
|
|
* Clear all notifications
|
|
*/
|
|
func clearAllNotifications() {
|
|
storage.clearAllNotifications()
|
|
}
|
|
|
|
// MARK: - Settings Operations
|
|
|
|
/**
|
|
* Save settings
|
|
*
|
|
* @param settings Settings dictionary
|
|
*/
|
|
func saveSettings(_ settings: [String: Any]) {
|
|
storage.saveSettings(settings)
|
|
}
|
|
|
|
/**
|
|
* Get settings
|
|
*
|
|
* @return Settings dictionary
|
|
*/
|
|
func getSettings() -> [String: Any] {
|
|
return storage.getSettings()
|
|
}
|
|
|
|
// MARK: - Background Task Tracking
|
|
|
|
/**
|
|
* Save last successful run timestamp
|
|
*
|
|
* @param timestamp Timestamp in milliseconds
|
|
*/
|
|
func saveLastSuccessfulRun(timestamp: Int64) {
|
|
storage.saveLastSuccessfulRun(timestamp: timestamp)
|
|
}
|
|
|
|
/**
|
|
* Get last successful run timestamp
|
|
*
|
|
* @return Timestamp in milliseconds or nil
|
|
*/
|
|
func getLastSuccessfulRun() -> Int64? {
|
|
return storage.getLastSuccessfulRun()
|
|
}
|
|
|
|
/**
|
|
* Save BGTask earliest begin date
|
|
*
|
|
* @param timestamp Timestamp in milliseconds
|
|
*/
|
|
func saveBGTaskEarliestBegin(timestamp: Int64) {
|
|
storage.saveBGTaskEarliestBegin(timestamp: timestamp)
|
|
}
|
|
|
|
/**
|
|
* Get BGTask earliest begin date
|
|
*
|
|
* @return Timestamp in milliseconds or nil
|
|
*/
|
|
func getBGTaskEarliestBegin() -> Int64? {
|
|
return storage.getBGTaskEarliestBegin()
|
|
}
|
|
|
|
// MARK: - Rolling Window Operations (Phase 2)
|
|
|
|
/**
|
|
* Maintain rolling window
|
|
*
|
|
* Phase 2: Rolling window maintenance
|
|
* Delegates to DailyNotificationRollingWindow for window maintenance
|
|
*/
|
|
func maintainRollingWindow() {
|
|
rollingWindow?.maintainRollingWindow()
|
|
}
|
|
|
|
// MARK: - TTL Enforcement Operations (Phase 2)
|
|
|
|
/**
|
|
* Validate content freshness before arming
|
|
*
|
|
* Phase 2: TTL validation
|
|
*
|
|
* @param content Notification content
|
|
* @return true if content is fresh
|
|
*/
|
|
func validateContentFreshness(_ content: NotificationContent) -> Bool {
|
|
guard let ttlEnforcer = ttlEnforcer else {
|
|
return true // No TTL enforcement if enforcer not available
|
|
}
|
|
|
|
return ttlEnforcer.validateBeforeArming(content)
|
|
}
|
|
}
|
|
|