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
This commit is contained in:
Server
2025-11-13 05:14:24 -08:00
parent 2d84ae29ba
commit 5844b92e18
61 changed files with 9676 additions and 356 deletions

View File

@@ -0,0 +1,210 @@
/**
* 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
*/
func maintainRollingWindow() {
// TODO: Phase 2 - Implement rolling window maintenance
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 {
// TODO: Phase 2 - Implement TTL validation
guard let ttlEnforcer = ttlEnforcer else {
return true // No TTL enforcement in Phase 1
}
// TODO: Call ttlEnforcer.validateBeforeArming(content)
return true
}
}