Files
daily-notification-plugin/ios/Plugin/DailyNotificationPerformanceOptimizer.swift
Server 5844b92e18 feat(ios): implement Phase 1 permission methods and fix build issues
Implement checkPermissionStatus() and requestNotificationPermissions()
methods for iOS plugin, matching Android functionality. Fix compilation
errors across plugin files and add comprehensive build/test infrastructure.

Key Changes:
- Add checkPermissionStatus() and requestNotificationPermissions() methods
- Fix 13+ categories of Swift compilation errors (type conversions, logger
  API, access control, async/await, etc.)
- Create DailyNotificationScheduler, DailyNotificationStorage,
  DailyNotificationStateActor, and DailyNotificationErrorCodes components
- Fix CoreData initialization to handle missing model gracefully for Phase 1
- Add iOS test app build script with simulator auto-detection
- Update directive with lessons learned from build and permission work

Build Status:  BUILD SUCCEEDED
Test App:  Ready for iOS Simulator testing

Files Modified:
- doc/directives/0003-iOS-Android-Parity-Directive.md (lessons learned)
- ios/Plugin/DailyNotificationPlugin.swift (Phase 1 methods)
- ios/Plugin/DailyNotificationModel.swift (CoreData fix)
- 11+ other plugin files (compilation fixes)

Files Added:
- ios/Plugin/DailyNotificationScheduler.swift
- ios/Plugin/DailyNotificationStorage.swift
- ios/Plugin/DailyNotificationStateActor.swift
- ios/Plugin/DailyNotificationErrorCodes.swift
- scripts/build-ios-test-app.sh
- scripts/setup-ios-test-app.sh
- test-apps/ios-test-app/ (full test app)
- Multiple Phase 1 documentation files
2025-11-13 05:14:24 -08:00

798 lines
27 KiB
Swift

/**
* DailyNotificationPerformanceOptimizer.swift
*
* iOS Performance Optimizer for database, memory, and battery optimization
* Implements query optimization, memory management, and battery tracking
*
* @author Matthew Raymer
* @version 1.0.0
*/
import Foundation
import os
/**
* Optimizes performance through database, memory, and battery management
*
* This class implements the critical performance optimization functionality:
* - Database query optimization with indexes
* - Memory usage monitoring and optimization
* - Object pooling for frequently used objects
* - Battery usage tracking and optimization
* - Background CPU usage minimization
* - Network request optimization
*/
class DailyNotificationPerformanceOptimizer {
// MARK: - Constants
private static let TAG = "DailyNotificationPerformanceOptimizer"
// Performance monitoring intervals
private static let MEMORY_CHECK_INTERVAL_SECONDS: TimeInterval = 5 * 60 // 5 minutes
private static let BATTERY_CHECK_INTERVAL_SECONDS: TimeInterval = 10 * 60 // 10 minutes
private static let PERFORMANCE_REPORT_INTERVAL_SECONDS: TimeInterval = 60 * 60 // 1 hour
// Memory thresholds
private static let MEMORY_WARNING_THRESHOLD_MB: Int = 50
private static let MEMORY_CRITICAL_THRESHOLD_MB: Int = 100
// Object pool sizes
private static let DEFAULT_POOL_SIZE = 10
private static let MAX_POOL_SIZE = 50
// MARK: - Properties
private let logger: DailyNotificationLogger
private let database: DailyNotificationDatabase
// Performance metrics
private let metrics = PerformanceMetrics()
// Object pools
private var objectPools: [String: ObjectPool] = [:]
private let poolQueue = DispatchQueue(label: "performance.pool", attributes: .concurrent)
// Memory monitoring
private var lastMemoryCheck: Date = Date()
private var lastBatteryCheck: Date = Date()
// MARK: - Initialization
/**
* Constructor
*
* @param logger Logger instance for debugging
* @param database Database instance for optimization
*/
init(logger: DailyNotificationLogger, database: DailyNotificationDatabase) {
self.logger = logger
self.database = database
// Initialize object pools
initializeObjectPools()
// Start performance monitoring
startPerformanceMonitoring()
logger.log(.debug, "\(DailyNotificationPerformanceOptimizer.TAG): PerformanceOptimizer initialized")
}
// MARK: - Database Optimization
/**
* Optimize database performance
*/
func optimizeDatabase() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Optimizing database performance")
// Add database indexes
addDatabaseIndexes()
// Optimize query performance
optimizeQueryPerformance()
// Implement connection pooling
optimizeConnectionPooling()
// Analyze database performance
analyzeDatabasePerformance()
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Database optimization completed")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error optimizing database: \(error)")
}
}
/**
* Add database indexes for query optimization
*/
private func addDatabaseIndexes() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Adding database indexes for query optimization")
// Add indexes for common queries
try database.executeSQL("CREATE INDEX IF NOT EXISTS idx_notif_contents_slot_time ON notif_contents(slot_id, fetched_at DESC)")
try database.executeSQL("CREATE INDEX IF NOT EXISTS idx_notif_deliveries_status ON notif_deliveries(status)")
try database.executeSQL("CREATE INDEX IF NOT EXISTS idx_notif_deliveries_fire_time ON notif_deliveries(fire_at)")
try database.executeSQL("CREATE INDEX IF NOT EXISTS idx_notif_config_key ON notif_config(k)")
// Add composite indexes for complex queries
try database.executeSQL("CREATE INDEX IF NOT EXISTS idx_notif_contents_slot_fetch ON notif_contents(slot_id, fetched_at)")
try database.executeSQL("CREATE INDEX IF NOT EXISTS idx_notif_deliveries_slot_status ON notif_deliveries(slot_id, status)")
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Database indexes added successfully")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error adding database indexes: \(error)")
}
}
/**
* Optimize query performance
*/
private func optimizeQueryPerformance() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Optimizing query performance")
// Set database optimization pragmas
try database.executeSQL("PRAGMA optimize")
try database.executeSQL("PRAGMA analysis_limit=1000")
try database.executeSQL("PRAGMA optimize")
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Query performance optimization completed")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error optimizing query performance: \(error)")
}
}
/**
* Optimize connection pooling
*/
private func optimizeConnectionPooling() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Optimizing connection pooling")
// Set connection pool settings
try database.executeSQL("PRAGMA cache_size=10000")
try database.executeSQL("PRAGMA temp_store=MEMORY")
try database.executeSQL("PRAGMA mmap_size=268435456") // 256MB
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Connection pooling optimization completed")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error optimizing connection pooling: \(error)")
}
}
/**
* Analyze database performance
*/
private func analyzeDatabasePerformance() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Analyzing database performance")
// Phase 1: Database stats methods not yet implemented
// TODO: Phase 2 - Implement database statistics
let pageCount: Int = 0
let pageSize: Int = 0
let cacheSize: Int = 0
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Database stats: pages=\(pageCount), pageSize=\(pageSize), cacheSize=\(cacheSize)")
// Phase 1: Metrics recording not yet implemented
// TODO: Phase 2 - Implement metrics recording
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error analyzing database performance: \(error)")
}
}
// MARK: - Memory Optimization
/**
* Optimize memory usage
*/
func optimizeMemory() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Optimizing memory usage")
// Check current memory usage
let memoryUsage = getCurrentMemoryUsage()
if memoryUsage > DailyNotificationPerformanceOptimizer.MEMORY_CRITICAL_THRESHOLD_MB {
logger.log(.warning, "DailyNotificationPerformanceOptimizer.TAG: Critical memory usage detected: \(memoryUsage)MB")
performCriticalMemoryCleanup()
} else if memoryUsage > DailyNotificationPerformanceOptimizer.MEMORY_WARNING_THRESHOLD_MB {
logger.log(.warning, "DailyNotificationPerformanceOptimizer.TAG: High memory usage detected: \(memoryUsage)MB")
performMemoryCleanup()
}
// Optimize object pools
optimizeObjectPools()
// Update metrics
metrics.recordMemoryUsage(memoryUsage)
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Memory optimization completed")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error optimizing memory: \(error)")
}
}
/**
* Get current memory usage in MB
*
* @return Memory usage in MB
*/
private func getCurrentMemoryUsage() -> Int {
do {
var info = mach_task_basic_info()
var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4
let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
}
}
if kerr == KERN_SUCCESS {
return Int(info.resident_size / 1024 / 1024) // Convert to MB
} else {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error getting memory usage: \(kerr)")
return 0
}
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error getting memory usage: \(error)")
return 0
}
}
/**
* Perform critical memory cleanup
*/
private func performCriticalMemoryCleanup() {
do {
logger.log(.warning, "DailyNotificationPerformanceOptimizer.TAG: Performing critical memory cleanup")
// Clear object pools
clearObjectPools()
// Clear caches
clearCaches()
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Critical memory cleanup completed")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error performing critical memory cleanup: \(error)")
}
}
/**
* Perform regular memory cleanup
*/
private func performMemoryCleanup() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Performing regular memory cleanup")
// Clean up expired objects in pools
cleanupObjectPools()
// Clear old caches
clearOldCaches()
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Regular memory cleanup completed")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error performing memory cleanup: \(error)")
}
}
// MARK: - Object Pooling
/**
* Initialize object pools
*/
private func initializeObjectPools() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Initializing object pools")
// Create pools for frequently used objects
createObjectPool(type: "String", initialSize: DailyNotificationPerformanceOptimizer.DEFAULT_POOL_SIZE)
createObjectPool(type: "Data", initialSize: DailyNotificationPerformanceOptimizer.DEFAULT_POOL_SIZE)
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Object pools initialized")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error initializing object pools: \(error)")
}
}
/**
* Create object pool for a type
*
* @param type Type to create pool for
* @param initialSize Initial pool size
*/
private func createObjectPool(type: String, initialSize: Int) {
do {
let pool = ObjectPool(type: type, maxSize: initialSize)
poolQueue.async(flags: .barrier) {
self.objectPools[type] = pool
}
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Object pool created for \(type) with size \(initialSize)")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error creating object pool for \(type): \(error)")
}
}
/**
* Get object from pool
*
* @param type Type of object to get
* @return Object from pool or new instance
*/
func getObject(type: String) -> Any? {
do {
var pool: ObjectPool?
poolQueue.sync {
pool = objectPools[type]
}
if let pool = pool {
return pool.getObject()
}
// Create new instance if no pool exists
return createNewObject(type: type)
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error getting object from pool: \(error)")
return nil
}
}
/**
* Return object to pool
*
* @param type Type of object
* @param object Object to return
*/
func returnObject(type: String, object: Any) {
do {
var pool: ObjectPool?
poolQueue.sync {
pool = objectPools[type]
}
if let pool = pool {
pool.returnObject(object)
}
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error returning object to pool: \(error)")
}
}
/**
* Create new object of specified type
*
* @param type Type to create
* @return New object instance
*/
private func createNewObject(type: String) -> Any? {
switch type {
case "String":
return ""
case "Data":
return Data()
default:
return nil
}
}
/**
* Optimize object pools
*/
private func optimizeObjectPools() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Optimizing object pools")
poolQueue.async(flags: .barrier) {
for pool in self.objectPools.values {
pool.optimize()
}
}
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Object pools optimized")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error optimizing object pools: \(error)")
}
}
/**
* Clean up object pools
*/
private func cleanupObjectPools() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Cleaning up object pools")
poolQueue.async(flags: .barrier) {
for pool in self.objectPools.values {
pool.cleanup()
}
}
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Object pools cleaned up")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error cleaning up object pools: \(error)")
}
}
/**
* Clear object pools
*/
private func clearObjectPools() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Clearing object pools")
poolQueue.async(flags: .barrier) {
for pool in self.objectPools.values {
pool.clear()
}
}
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Object pools cleared")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error clearing object pools: \(error)")
}
}
// MARK: - Battery Optimization
/**
* Optimize battery usage
*/
func optimizeBattery() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Optimizing battery usage")
// Minimize background CPU usage
minimizeBackgroundCPUUsage()
// Optimize network requests
optimizeNetworkRequests()
// Track battery usage
trackBatteryUsage()
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Battery optimization completed")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error optimizing battery: \(error)")
}
}
/**
* Minimize background CPU usage
*/
private func minimizeBackgroundCPUUsage() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Minimizing background CPU usage")
// Reduce background task frequency
// This would adjust task intervals based on battery level
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Background CPU usage minimized")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error minimizing background CPU usage: \(error)")
}
}
/**
* Optimize network requests
*/
private func optimizeNetworkRequests() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Optimizing network requests")
// Batch network requests when possible
// Reduce request frequency during low battery
// Use efficient data formats
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Network requests optimized")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error optimizing network requests: \(error)")
}
}
/**
* Track battery usage
*/
private func trackBatteryUsage() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Tracking battery usage")
// This would integrate with battery monitoring APIs
// Track battery consumption patterns
// Adjust behavior based on battery level
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Battery usage tracking completed")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error tracking battery usage: \(error)")
}
}
// MARK: - Performance Monitoring
/**
* Start performance monitoring
*/
private func startPerformanceMonitoring() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Starting performance monitoring")
// Schedule memory monitoring
Timer.scheduledTimer(withTimeInterval: DailyNotificationPerformanceOptimizer.MEMORY_CHECK_INTERVAL_SECONDS, repeats: true) { _ in
self.checkMemoryUsage()
}
// Schedule battery monitoring
Timer.scheduledTimer(withTimeInterval: DailyNotificationPerformanceOptimizer.BATTERY_CHECK_INTERVAL_SECONDS, repeats: true) { _ in
self.checkBatteryUsage()
}
// Schedule performance reporting
Timer.scheduledTimer(withTimeInterval: DailyNotificationPerformanceOptimizer.PERFORMANCE_REPORT_INTERVAL_SECONDS, repeats: true) { _ in
self.reportPerformance()
}
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Performance monitoring started")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error starting performance monitoring: \(error)")
}
}
/**
* Check memory usage
*/
private func checkMemoryUsage() {
do {
let currentTime = Date()
if currentTime.timeIntervalSince(lastMemoryCheck) < DailyNotificationPerformanceOptimizer.MEMORY_CHECK_INTERVAL_SECONDS {
return
}
lastMemoryCheck = currentTime
let memoryUsage = getCurrentMemoryUsage()
metrics.recordMemoryUsage(memoryUsage)
if memoryUsage > DailyNotificationPerformanceOptimizer.MEMORY_WARNING_THRESHOLD_MB {
logger.log(.warning, "DailyNotificationPerformanceOptimizer.TAG: High memory usage detected: \(memoryUsage)MB")
optimizeMemory()
}
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error checking memory usage: \(error)")
}
}
/**
* Check battery usage
*/
private func checkBatteryUsage() {
do {
let currentTime = Date()
if currentTime.timeIntervalSince(lastBatteryCheck) < DailyNotificationPerformanceOptimizer.BATTERY_CHECK_INTERVAL_SECONDS {
return
}
lastBatteryCheck = currentTime
// This would check actual battery usage
// For now, we'll just log the check
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Battery usage check performed")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error checking battery usage: \(error)")
}
}
/**
* Report performance metrics
*/
private func reportPerformance() {
do {
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Performance Report:")
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Memory Usage: \(metrics.getAverageMemoryUsage())MB")
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Database Queries: \(metrics.getTotalDatabaseQueries())")
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Object Pool Hits: \(metrics.getObjectPoolHits())")
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Performance Score: \(metrics.getPerformanceScore())")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error reporting performance: \(error)")
}
}
// MARK: - Utility Methods
/**
* Clear caches
*/
private func clearCaches() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Clearing caches")
// Clear database caches
try database.executeSQL("PRAGMA cache_size=0")
try database.executeSQL("PRAGMA cache_size=1000")
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Caches cleared")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error clearing caches: \(error)")
}
}
/**
* Clear old caches
*/
private func clearOldCaches() {
do {
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Clearing old caches")
// This would clear old cache entries
// For now, we'll just log the action
logger.log(.info, "DailyNotificationPerformanceOptimizer.TAG: Old caches cleared")
} catch {
logger.log(.error, "DailyNotificationPerformanceOptimizer.TAG: Error clearing old caches: \(error)")
}
}
// MARK: - Public API
/**
* Get performance metrics
*
* @return PerformanceMetrics with current statistics
*/
func getMetrics() -> PerformanceMetrics {
return metrics
}
/**
* Reset performance metrics
*/
func resetMetrics() {
metrics.reset()
logger.log(.debug, "DailyNotificationPerformanceOptimizer.TAG: Performance metrics reset")
}
// MARK: - Data Classes
/**
* Object pool for managing object reuse
*/
private class ObjectPool {
let type: String
private var pool: [Any] = []
private let maxSize: Int
private var currentSize: Int = 0
init(type: String, maxSize: Int) {
self.type = type
self.maxSize = maxSize
}
func getObject() -> Any? {
if !pool.isEmpty {
let object = pool.removeFirst()
currentSize -= 1
return object
}
return nil
}
func returnObject(_ object: Any) {
if currentSize < maxSize {
pool.append(object)
currentSize += 1
}
}
func optimize() {
// Remove excess objects
while currentSize > maxSize / 2 && !pool.isEmpty {
pool.removeFirst()
currentSize -= 1
}
}
func cleanup() {
pool.removeAll()
currentSize = 0
}
func clear() {
pool.removeAll()
currentSize = 0
}
}
/**
* Performance metrics
*/
class PerformanceMetrics {
private var totalMemoryUsage: Int = 0
private var memoryCheckCount: Int = 0
private var totalDatabaseQueries: Int = 0
private var objectPoolHits: Int = 0
private var performanceScore: Int = 100
func recordMemoryUsage(_ usage: Int) {
totalMemoryUsage += usage
memoryCheckCount += 1
}
func recordDatabaseQuery() {
totalDatabaseQueries += 1
}
func recordObjectPoolHit() {
objectPoolHits += 1
}
func updatePerformanceScore(_ score: Int) {
performanceScore = max(0, min(100, score))
}
func recordDatabaseStats(pageCount: Int, pageSize: Int, cacheSize: Int) {
// Update performance score based on database stats
let score = max(0, min(100, 100 - (pageCount / 1000)))
updatePerformanceScore(score)
}
func reset() {
totalMemoryUsage = 0
memoryCheckCount = 0
totalDatabaseQueries = 0
objectPoolHits = 0
performanceScore = 100
}
func getAverageMemoryUsage() -> Int {
return memoryCheckCount > 0 ? totalMemoryUsage / memoryCheckCount : 0
}
func getTotalDatabaseQueries() -> Int {
return totalDatabaseQueries
}
func getObjectPoolHits() -> Int {
return objectPoolHits
}
func getPerformanceScore() -> Int {
return performanceScore
}
}
}