fix(ios,android): implement rolling window counting, TTL validation, and DB persistence
- iOS: Implement rolling window counting using UNUserNotificationCenter - iOS: Enable TTL validation in scheduler before arming notifications - iOS: Implement SQLite persistence for save/delete/clear operations - Android: Implement rolling window counting using storage as source of truth - Android: Add dateBoundsMillis helper for date range calculations Removes all TODO stubs affecting capacity/rate-limiting correctness. Fixes runtime behavior to match test expectations and optimizer logic. Refs: Deep fixes directive for bottom-of-tree gaps
This commit is contained in:
@@ -287,6 +287,25 @@ class DailyNotificationRollingWindow {
|
||||
|
||||
// MARK: - Data Access
|
||||
|
||||
/**
|
||||
* Fetch pending notification requests synchronously
|
||||
*
|
||||
* @param timeoutSeconds Timeout in seconds
|
||||
* @return Array of pending notification requests
|
||||
*/
|
||||
private func fetchPendingRequestsSync(timeoutSeconds: TimeInterval) -> [UNNotificationRequest] {
|
||||
let sem = DispatchSemaphore(value: 0)
|
||||
var result: [UNNotificationRequest] = []
|
||||
|
||||
UNUserNotificationCenter.current().getPendingNotificationRequests { requests in
|
||||
result = requests
|
||||
sem.signal()
|
||||
}
|
||||
|
||||
_ = sem.wait(timeout: .now() + timeoutSeconds)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Count pending notifications
|
||||
*
|
||||
@@ -294,10 +313,8 @@ class DailyNotificationRollingWindow {
|
||||
*/
|
||||
private func countPendingNotifications() -> Int {
|
||||
do {
|
||||
// This would typically query the storage for pending notifications
|
||||
// For now, we'll use a placeholder implementation
|
||||
return 0 // TODO: Implement actual counting logic
|
||||
|
||||
let requests = fetchPendingRequestsSync(timeoutSeconds: 2.0)
|
||||
return requests.count
|
||||
} catch {
|
||||
print("\(Self.TAG): Error counting pending notifications: \(error)")
|
||||
return 0
|
||||
@@ -312,10 +329,18 @@ class DailyNotificationRollingWindow {
|
||||
*/
|
||||
private func countNotificationsForDate(_ date: String) -> Int {
|
||||
do {
|
||||
// This would typically query the storage for notifications on a specific date
|
||||
// For now, we'll use a placeholder implementation
|
||||
return 0 // TODO: Implement actual counting logic
|
||||
|
||||
let requests = fetchPendingRequestsSync(timeoutSeconds: 2.0)
|
||||
|
||||
var count = 0
|
||||
for req in requests {
|
||||
guard let trigger = req.trigger as? UNCalendarNotificationTrigger else { continue }
|
||||
guard let nextDate = trigger.nextTriggerDate() else { continue }
|
||||
if formatDate(nextDate) == date {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
} catch {
|
||||
print("\(Self.TAG): Error counting notifications for date: \(date), error: \(error)")
|
||||
return 0
|
||||
@@ -330,10 +355,42 @@ class DailyNotificationRollingWindow {
|
||||
*/
|
||||
private func getNotificationsForDate(_ date: String) -> [NotificationContent] {
|
||||
do {
|
||||
// This would typically query the storage for notifications on a specific date
|
||||
// For now, we'll return an empty array
|
||||
return [] // TODO: Implement actual retrieval logic
|
||||
|
||||
let requests = fetchPendingRequestsSync(timeoutSeconds: 2.0)
|
||||
|
||||
var results: [NotificationContent] = []
|
||||
for req in requests {
|
||||
guard let trigger = req.trigger as? UNCalendarNotificationTrigger else { continue }
|
||||
guard let nextDate = trigger.nextTriggerDate() else { continue }
|
||||
if formatDate(nextDate) != date { continue }
|
||||
|
||||
// We cannot reconstruct full NotificationContent from UNNotificationRequest reliably,
|
||||
// so this returns minimal stubs primarily for internal rolling-window inspection.
|
||||
let id = req.identifier
|
||||
let scheduledMs = Int64(nextDate.timeIntervalSince1970 * 1000.0)
|
||||
|
||||
let fetchedMs: Int64
|
||||
if let fetchedAt = req.content.userInfo["fetched_at"] as? Int64 {
|
||||
fetchedMs = fetchedAt
|
||||
} else if let fetchedAt = req.content.userInfo["fetched_at"] as? Int {
|
||||
fetchedMs = Int64(fetchedAt)
|
||||
} else {
|
||||
fetchedMs = scheduledMs
|
||||
}
|
||||
|
||||
let stub = NotificationContent(
|
||||
id: id,
|
||||
title: req.content.title,
|
||||
body: req.content.body,
|
||||
scheduledTime: scheduledMs,
|
||||
fetchedAt: fetchedMs,
|
||||
url: nil,
|
||||
payload: nil,
|
||||
etag: nil
|
||||
)
|
||||
results.append(stub)
|
||||
}
|
||||
|
||||
return results
|
||||
} catch {
|
||||
print("\(Self.TAG): Error getting notifications for date: \(date), error: \(error)")
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user