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:
210
ios/Plugin/DailyNotificationStateActor.swift
Normal file
210
ios/Plugin/DailyNotificationStateActor.swift
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user