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
176 lines
4.9 KiB
Swift
176 lines
4.9 KiB
Swift
//
|
|
// DailyNotificationModel.xcdatamodeld
|
|
// DailyNotificationPlugin
|
|
//
|
|
// Created by Matthew Raymer on 2025-09-22
|
|
// Copyright © 2025 TimeSafari. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import CoreData
|
|
|
|
/**
|
|
* Core Data model for Daily Notification Plugin
|
|
* Mirrors Android SQLite schema for cross-platform consistency
|
|
*
|
|
* @author Matthew Raymer
|
|
* @version 1.1.0
|
|
* @created 2025-09-22 09:22:32 UTC
|
|
*/
|
|
|
|
// MARK: - ContentCache Entity
|
|
@objc(ContentCache)
|
|
public class ContentCache: NSManagedObject {
|
|
|
|
}
|
|
|
|
extension ContentCache {
|
|
@nonobjc public class func fetchRequest() -> NSFetchRequest<ContentCache> {
|
|
return NSFetchRequest<ContentCache>(entityName: "ContentCache")
|
|
}
|
|
|
|
@NSManaged public var id: String?
|
|
@NSManaged public var fetchedAt: Date?
|
|
@NSManaged public var ttlSeconds: Int32
|
|
@NSManaged public var payload: Data?
|
|
@NSManaged public var meta: String?
|
|
}
|
|
|
|
extension ContentCache: Identifiable {
|
|
|
|
}
|
|
|
|
// MARK: - Schedule Entity
|
|
@objc(Schedule)
|
|
public class Schedule: NSManagedObject {
|
|
|
|
}
|
|
|
|
extension Schedule {
|
|
@nonobjc public class func fetchRequest() -> NSFetchRequest<Schedule> {
|
|
return NSFetchRequest<Schedule>(entityName: "Schedule")
|
|
}
|
|
|
|
@NSManaged public var id: String?
|
|
@NSManaged public var kind: String?
|
|
@NSManaged public var cron: String?
|
|
@NSManaged public var clockTime: String?
|
|
@NSManaged public var enabled: Bool
|
|
@NSManaged public var lastRunAt: Date?
|
|
@NSManaged public var nextRunAt: Date?
|
|
@NSManaged public var jitterMs: Int32
|
|
@NSManaged public var backoffPolicy: String?
|
|
@NSManaged public var stateJson: String?
|
|
}
|
|
|
|
extension Schedule: Identifiable {
|
|
|
|
}
|
|
|
|
// MARK: - Callback Entity
|
|
@objc(Callback)
|
|
public class Callback: NSManagedObject {
|
|
|
|
}
|
|
|
|
extension Callback {
|
|
@nonobjc public class func fetchRequest() -> NSFetchRequest<Callback> {
|
|
return NSFetchRequest<Callback>(entityName: "Callback")
|
|
}
|
|
|
|
@NSManaged public var id: String?
|
|
@NSManaged public var kind: String?
|
|
@NSManaged public var target: String?
|
|
@NSManaged public var headersJson: String?
|
|
@NSManaged public var enabled: Bool
|
|
@NSManaged public var createdAt: Date?
|
|
}
|
|
|
|
extension Callback: Identifiable {
|
|
|
|
}
|
|
|
|
// MARK: - History Entity
|
|
@objc(History)
|
|
public class History: NSManagedObject {
|
|
|
|
}
|
|
|
|
extension History {
|
|
@nonobjc public class func fetchRequest() -> NSFetchRequest<History> {
|
|
return NSFetchRequest<History>(entityName: "History")
|
|
}
|
|
|
|
@NSManaged public var id: String?
|
|
@NSManaged public var refId: String?
|
|
@NSManaged public var kind: String?
|
|
@NSManaged public var occurredAt: Date?
|
|
@NSManaged public var durationMs: Int32
|
|
@NSManaged public var outcome: String?
|
|
@NSManaged public var diagJson: String?
|
|
}
|
|
|
|
extension History: Identifiable {
|
|
|
|
}
|
|
|
|
// MARK: - Persistence Controller
|
|
// Phase 2: CoreData integration for advanced features
|
|
// Phase 1: Stubbed out - CoreData model not yet created
|
|
class PersistenceController {
|
|
// Lazy initialization to prevent Phase 1 errors
|
|
private static var _shared: PersistenceController?
|
|
static var shared: PersistenceController {
|
|
if _shared == nil {
|
|
_shared = PersistenceController()
|
|
}
|
|
return _shared!
|
|
}
|
|
|
|
let container: NSPersistentContainer?
|
|
private var initializationError: Error?
|
|
|
|
init(inMemory: Bool = false) {
|
|
// Phase 1: CoreData model doesn't exist yet, so we'll handle gracefully
|
|
// Phase 2: Will create DailyNotificationModel.xcdatamodeld
|
|
var tempContainer: NSPersistentContainer? = nil
|
|
|
|
do {
|
|
tempContainer = NSPersistentContainer(name: "DailyNotificationModel")
|
|
|
|
if inMemory {
|
|
tempContainer?.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
|
|
}
|
|
|
|
var loadError: Error? = nil
|
|
tempContainer?.loadPersistentStores { _, error in
|
|
if let error = error as NSError? {
|
|
loadError = error
|
|
print("DNP-PLUGIN: CoreData model not found (Phase 1 - expected). Error: \(error.localizedDescription)")
|
|
print("DNP-PLUGIN: CoreData features will be available in Phase 2")
|
|
}
|
|
}
|
|
|
|
if let error = loadError {
|
|
self.initializationError = error
|
|
self.container = nil
|
|
} else {
|
|
tempContainer?.viewContext.automaticallyMergesChangesFromParent = true
|
|
self.container = tempContainer
|
|
}
|
|
} catch {
|
|
print("DNP-PLUGIN: Failed to initialize CoreData container: \(error.localizedDescription)")
|
|
self.initializationError = error
|
|
self.container = nil
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if CoreData is available (Phase 2+)
|
|
*/
|
|
var isAvailable: Bool {
|
|
return container != nil && initializationError == nil
|
|
}
|
|
}
|
|
|