feat(ios): implement Phase 2.1 iOS background tasks with T–lead prefetch
- Add DailyNotificationBackgroundTaskManager with BGTaskScheduler integration - Add DailyNotificationTTLEnforcer for iOS freshness validation - Add DailyNotificationRollingWindow for iOS capacity management - Add DailyNotificationDatabase with SQLite schema and WAL mode - Add NotificationContent data structure for iOS - Update DailyNotificationPlugin with background task integration - Add phase2-1-ios-background-tasks.ts usage examples This implements the critical Phase 2.1 iOS background execution: - BGTaskScheduler integration for T–lead prefetch - Single-attempt prefetch with 12s timeout - ETag/304 caching support for efficient content updates - Background execution constraints handling - Integration with existing TTL enforcement and rolling window - iOS-specific capacity limits and notification management Files: 7 changed, 2088 insertions(+), 299 deletions(-)
This commit is contained in:
170
ios/Plugin/NotificationContent.swift
Normal file
170
ios/Plugin/NotificationContent.swift
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* NotificationContent.swift
|
||||
*
|
||||
* Data structure for notification content
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
* Data structure representing notification content
|
||||
*
|
||||
* This class encapsulates all the information needed for a notification
|
||||
* including scheduling, content, and metadata.
|
||||
*/
|
||||
class NotificationContent {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
let id: String
|
||||
let title: String?
|
||||
let body: String?
|
||||
let scheduledTime: TimeInterval // milliseconds since epoch
|
||||
let fetchedAt: TimeInterval // milliseconds since epoch
|
||||
let url: String?
|
||||
let payload: [String: Any]?
|
||||
let etag: String?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/**
|
||||
* Initialize notification content
|
||||
*
|
||||
* @param id Unique notification identifier
|
||||
* @param title Notification title
|
||||
* @param body Notification body text
|
||||
* @param scheduledTime When notification should fire (milliseconds since epoch)
|
||||
* @param fetchedAt When content was fetched (milliseconds since epoch)
|
||||
* @param url URL for content fetching
|
||||
* @param payload Additional payload data
|
||||
* @param etag ETag for HTTP caching
|
||||
*/
|
||||
init(id: String,
|
||||
title: String?,
|
||||
body: String?,
|
||||
scheduledTime: TimeInterval,
|
||||
fetchedAt: TimeInterval,
|
||||
url: String?,
|
||||
payload: [String: Any]?,
|
||||
etag: String?) {
|
||||
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.body = body
|
||||
self.scheduledTime = scheduledTime
|
||||
self.fetchedAt = fetchedAt
|
||||
self.url = url
|
||||
self.payload = payload
|
||||
self.etag = etag
|
||||
}
|
||||
|
||||
// MARK: - Convenience Methods
|
||||
|
||||
/**
|
||||
* Get scheduled time as Date
|
||||
*
|
||||
* @return Scheduled time as Date object
|
||||
*/
|
||||
func getScheduledTimeAsDate() -> Date {
|
||||
return Date(timeIntervalSince1970: scheduledTime / 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fetched time as Date
|
||||
*
|
||||
* @return Fetched time as Date object
|
||||
*/
|
||||
func getFetchedTimeAsDate() -> Date {
|
||||
return Date(timeIntervalSince1970: fetchedAt / 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if notification is scheduled for today
|
||||
*
|
||||
* @return true if scheduled for today
|
||||
*/
|
||||
func isScheduledForToday() -> Bool {
|
||||
let scheduledDate = getScheduledTimeAsDate()
|
||||
let today = Date()
|
||||
|
||||
let calendar = Calendar.current
|
||||
return calendar.isDate(scheduledDate, inSameDayAs: today)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if notification is scheduled for tomorrow
|
||||
*
|
||||
* @return true if scheduled for tomorrow
|
||||
*/
|
||||
func isScheduledForTomorrow() -> Bool {
|
||||
let scheduledDate = getScheduledTimeAsDate()
|
||||
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date()) ?? Date()
|
||||
|
||||
let calendar = Calendar.current
|
||||
return calendar.isDate(scheduledDate, inSameDayAs: tomorrow)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if notification is in the future
|
||||
*
|
||||
* @return true if scheduled time is in the future
|
||||
*/
|
||||
func isInTheFuture() -> Bool {
|
||||
return scheduledTime > Date().timeIntervalSince1970 * 1000
|
||||
}
|
||||
|
||||
/**
|
||||
* Get age of content at scheduled time
|
||||
*
|
||||
* @return Age in seconds at scheduled time
|
||||
*/
|
||||
func getAgeAtScheduledTime() -> TimeInterval {
|
||||
return (scheduledTime - fetchedAt) / 1000
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to dictionary representation
|
||||
*
|
||||
* @return Dictionary representation of notification content
|
||||
*/
|
||||
func toDictionary() -> [String: Any] {
|
||||
return [
|
||||
"id": id,
|
||||
"title": title ?? "",
|
||||
"body": body ?? "",
|
||||
"scheduledTime": scheduledTime,
|
||||
"fetchedAt": fetchedAt,
|
||||
"url": url ?? "",
|
||||
"payload": payload ?? [:],
|
||||
"etag": etag ?? ""
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from dictionary representation
|
||||
*
|
||||
* @param dict Dictionary representation
|
||||
* @return NotificationContent instance
|
||||
*/
|
||||
static func fromDictionary(_ dict: [String: Any]) -> NotificationContent? {
|
||||
guard let id = dict["id"] as? String,
|
||||
let scheduledTime = dict["scheduledTime"] as? TimeInterval,
|
||||
let fetchedAt = dict["fetchedAt"] as? TimeInterval else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NotificationContent(
|
||||
id: id,
|
||||
title: dict["title"] as? String,
|
||||
body: dict["body"] as? String,
|
||||
scheduledTime: scheduledTime,
|
||||
fetchedAt: fetchedAt,
|
||||
url: dict["url"] as? String,
|
||||
payload: dict["payload"] as? [String: Any],
|
||||
etag: dict["etag"] as? String
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user