Files
daily-notification-plugin/ios/Plugin/HistoryDAO.swift
Jose Olarte III 1bfd87a0e4 fix(ios): resolve build errors and add missing configureNativeFetcher method
Fixed Swift compilation errors preventing iOS build:
- Added explicit self capture [self] in closures in DailyNotificationReactivationManager
- Removed invalid BGTaskScheduler.shared.registeredTaskIdentifiers API call
- Fixed initialization order in DailyNotificationModel (verifyEntities after container init)

Added missing configureNativeFetcher method to iOS plugin:
- Implemented method matching Android functionality
- Stores configuration in UserDefaults for persistence
- Registered method in pluginMethods array
- Supports both jwtToken and jwtSecret parameters for compatibility

This resolves the runtime error "configureNativeFetcher is not a function"
that was preventing the test app from configuring the plugin.
2025-12-11 16:44:18 +08:00

301 lines
9.4 KiB
Swift

/**
* HistoryDAO.swift
*
* Data Access Object (DAO) for History Core Data entity
* Provides helper methods for recording recovery and operation history
*
* @author Matthew Raymer
* @version 1.0.0
* @created 2025-12-08
*/
import Foundation
import CoreData
/**
* Extension providing DAO methods for History entity
*
* This extension adds helper methods for recording operation history
* including recovery operations, errors, and metrics.
*/
extension History {
// MARK: - Constants
private static let TAG = "DNP-HISTORY-DAO"
// MARK: - Create/Insert Methods
/**
* Create a new History entity in the given context
*
* @param context Core Data managed object context
* @param id Unique history identifier (UUID recommended)
* @param refId Reference ID (e.g., notification ID, schedule ID)
* @param kind History kind (e.g., "recovery", "fetch", "notify")
* @param occurredAt When the event occurred
* @param durationMs Duration in milliseconds
* @param outcome Outcome string (e.g., "success", "failure", "skipped")
* @param diagJson Diagnostic JSON string with additional details
* @return Created History entity
*/
static func create(
in context: NSManagedObjectContext,
id: String,
refId: String? = nil,
kind: String,
occurredAt: Date,
durationMs: Int32 = 0,
outcome: String,
diagJson: String? = nil
) -> History {
let entity = History(context: context)
entity.id = id
entity.refId = refId
entity.kind = kind
entity.occurredAt = occurredAt
entity.durationMs = durationMs
entity.outcome = outcome
entity.diagJson = diagJson
print("\(Self.TAG): Created History record: kind=\(kind), outcome=\(outcome)")
return entity
}
/**
* Create history record from dictionary
*
* @param context Core Data managed object context
* @param dict Dictionary with history data
* @return Created History entity or nil
*/
static func create(
in context: NSManagedObjectContext,
from dict: [String: Any]
) -> History? {
guard let id = dict["id"] as? String,
let kind = dict["kind"] as? String,
let outcome = dict["outcome"] as? String else {
print("\(Self.TAG): Missing required fields")
return nil
}
// Convert occurredAt from epoch milliseconds or Date
let occurredAt: Date
if let timeMillis = dict["occurredAt"] as? Int64 {
occurredAt = DailyNotificationDataConversions.dateFromEpochMillis(timeMillis)
} else if let timeDate = dict["occurredAt"] as? Date {
occurredAt = timeDate
} else {
occurredAt = Date()
}
let entity = History(context: context)
entity.id = id
entity.refId = dict["refId"] as? String
entity.kind = kind
entity.occurredAt = occurredAt
entity.durationMs = DailyNotificationDataConversions.int32FromInt(
dict["durationMs"] as? Int ?? 0
)
entity.outcome = outcome
// Convert diagJson from dictionary if needed
if let diagDict = dict["diagJson"] as? [String: Any] {
entity.diagJson = DailyNotificationDataConversions.jsonStringFromDictionary(diagDict)
} else if let diagString = dict["diagJson"] as? String {
entity.diagJson = diagString
}
return entity
}
/**
* Record recovery history with metrics
*
* @param context Core Data managed object context
* @param scenario Recovery scenario
* @param missedCount Number of missed notifications
* @param rescheduledCount Number of rescheduled notifications
* @param verifiedCount Number of verified notifications
* @param errors Number of errors
* @param startTime When recovery started
* @param endTime When recovery ended
* @return Created History entity
*/
static func recordRecovery(
in context: NSManagedObjectContext,
scenario: String,
missedCount: Int,
rescheduledCount: Int,
verifiedCount: Int,
errors: Int,
startTime: Date,
endTime: Date
) -> History {
let durationMs = Int32((endTime.timeIntervalSince(startTime) * 1000).rounded())
let diagJson: [String: Any] = [
"scenario": scenario,
"missedCount": missedCount,
"rescheduledCount": rescheduledCount,
"verifiedCount": verifiedCount,
"errors": errors,
"durationMs": durationMs
]
let diagJsonString = DailyNotificationDataConversions.jsonStringFromDictionary(diagJson) ?? "{}"
return create(
in: context,
id: UUID().uuidString,
kind: "recovery",
occurredAt: endTime,
durationMs: durationMs,
outcome: errors > 0 ? "partial_success" : "success",
diagJson: diagJsonString
)
}
/**
* Record recovery failure
*
* @param context Core Data managed object context
* @param error Error that occurred
* @param scenario Recovery scenario (if known)
* @return Created History entity
*/
static func recordRecoveryFailure(
in context: NSManagedObjectContext,
error: Error,
scenario: String? = nil
) -> History {
var errorInfo: [String: Any] = [
"error": error.localizedDescription,
"errorType": String(describing: type(of: error))
]
// Add scenario if provided
if let scenario = scenario {
errorInfo["scenario"] = scenario
}
// Add error details if available
if let nsError = error as NSError? {
errorInfo["errorCode"] = nsError.code
errorInfo["errorDomain"] = nsError.domain
if let userInfo = nsError.userInfo as? [String: Any] {
errorInfo["userInfo"] = userInfo
}
}
let diagJsonString = DailyNotificationDataConversions.jsonStringFromDictionary(errorInfo) ?? "{}"
return create(
in: context,
id: UUID().uuidString,
kind: "recovery",
occurredAt: Date(),
outcome: "failure",
diagJson: diagJsonString
)
}
// MARK: - Read/Query Methods
/**
* Fetch History by ID
*
* @param context Core Data managed object context
* @param id History ID
* @return History entity or nil
*/
static func fetch(
by id: String,
in context: NSManagedObjectContext
) -> History? {
let request: NSFetchRequest<History> = History.fetchRequest()
request.predicate = NSPredicate(format: "id == %@", id)
request.fetchLimit = 1
do {
let results = try context.fetch(request)
return results.first
} catch {
print("\(Self.TAG): Error fetching by id: \(error.localizedDescription)")
return nil
}
}
/**
* Query by kind
*
* @param context Core Data managed object context
* @param kind History kind
* @return Array of History entities
*/
static func query(
by kind: String,
in context: NSManagedObjectContext
) -> [History] {
let request: NSFetchRequest<History> = History.fetchRequest()
request.predicate = NSPredicate(format: "kind == %@", kind)
request.sortDescriptors = [NSSortDescriptor(key: "occurredAt", ascending: false)]
do {
return try context.fetch(request)
} catch {
print("\(Self.TAG): Error querying by kind: \(error.localizedDescription)")
return []
}
}
/**
* Query by refId
*
* @param context Core Data managed object context
* @param refId Reference ID
* @return Array of History entities
*/
static func queryByRefId(
_ refId: String,
in context: NSManagedObjectContext
) -> [History] {
let request: NSFetchRequest<History> = History.fetchRequest()
request.predicate = NSPredicate(format: "refId == %@", refId)
request.sortDescriptors = [NSSortDescriptor(key: "occurredAt", ascending: false)]
do {
return try context.fetch(request)
} catch {
print("\(Self.TAG): Error querying by refId: \(error.localizedDescription)")
return []
}
}
/**
* Query by outcome
*
* @param context Core Data managed object context
* @param outcome Outcome string
* @return Array of History entities
*/
static func queryByOutcome(
_ outcome: String,
in context: NSManagedObjectContext
) -> [History] {
let request: NSFetchRequest<History> = History.fetchRequest()
request.predicate = NSPredicate(format: "outcome == %@", outcome)
request.sortDescriptors = [NSSortDescriptor(key: "occurredAt", ascending: false)]
do {
return try context.fetch(request)
} catch {
print("\(Self.TAG): Error querying by outcome: \(error.localizedDescription)")
return []
}
}
}