Yattee v2 rewrite

This commit is contained in:
Arkadiusz Fal
2026-02-08 18:31:16 +01:00
parent 20d0cfc0c7
commit 05f921d605
1043 changed files with 163875 additions and 68430 deletions

View File

@@ -0,0 +1,174 @@
//
// BackgroundRefreshManager.swift
// Yattee
//
// Platform-specific background refresh orchestration.
//
import Foundation
#if os(iOS)
import BackgroundTasks
#endif
/// Manages background refresh scheduling and execution across platforms.
@MainActor
final class BackgroundRefreshManager {
// MARK: - Constants
static let backgroundTaskIdentifier = AppIdentifiers.backgroundFeedRefresh
#if os(macOS)
private var activityScheduler: NSBackgroundActivityScheduler?
#endif
// MARK: - Dependencies
private weak var appEnvironment: AppEnvironment?
private let backgroundRefresher: BackgroundFeedRefresher
private let notificationManager: NotificationManager
// MARK: - Initialization
init(notificationManager: NotificationManager) {
self.notificationManager = notificationManager
self.backgroundRefresher = BackgroundFeedRefresher(notificationManager: notificationManager)
}
func setAppEnvironment(_ environment: AppEnvironment) {
self.appEnvironment = environment
backgroundRefresher.setAppEnvironment(environment)
}
// MARK: - Registration (call at app launch)
func registerBackgroundTasks() {
#if os(iOS)
registerIOSBackgroundTask()
#elseif os(macOS)
registerMacOSBackgroundActivity()
#endif
}
// MARK: - iOS Implementation
#if os(iOS)
private func registerIOSBackgroundTask() {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: Self.backgroundTaskIdentifier,
using: nil
) { [weak self] task in
guard let task = task as? BGAppRefreshTask else { return }
self?.handleIOSBackgroundTask(task)
}
LoggingService.shared.info("Registered iOS background task", category: .notifications)
}
func scheduleIOSBackgroundRefresh() {
let request = BGAppRefreshTaskRequest(identifier: Self.backgroundTaskIdentifier)
// Request to run in ~15 minutes (system decides actual timing)
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
do {
try BGTaskScheduler.shared.submit(request)
LoggingService.shared.info("Scheduled iOS background refresh", category: .notifications)
} catch {
LoggingService.shared.logNotificationError("Failed to schedule background refresh", error: error)
}
}
func cancelIOSBackgroundRefresh() {
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: Self.backgroundTaskIdentifier)
LoggingService.shared.debug("Cancelled iOS background refresh", category: .notifications)
}
private func handleIOSBackgroundTask(_ task: BGAppRefreshTask) {
LoggingService.shared.info("iOS background task started", category: .notifications)
// Schedule the next refresh immediately
scheduleIOSBackgroundRefresh()
// Create a task to perform the refresh
let refreshTask = Task { @MainActor in
await backgroundRefresher.performBackgroundRefresh()
}
// Set expiration handler
task.expirationHandler = {
LoggingService.shared.warning("iOS background task expired", category: .notifications)
refreshTask.cancel()
}
// Wait for completion
Task {
_ = await refreshTask.result
task.setTaskCompleted(success: !refreshTask.isCancelled)
LoggingService.shared.info("iOS background task completed", category: .notifications)
}
}
#endif
// MARK: - macOS Implementation
#if os(macOS)
private func registerMacOSBackgroundActivity() {
let scheduler = NSBackgroundActivityScheduler(identifier: Self.backgroundTaskIdentifier)
scheduler.repeats = true
#if DEBUG
scheduler.interval = 60 // 1 minute for testing
scheduler.tolerance = 30
#else
scheduler.interval = 15 * 60 // 15 minutes
scheduler.tolerance = 5 * 60 // 5 minute tolerance
#endif
scheduler.qualityOfService = .utility
scheduler.schedule { [weak self] completion in
guard let self else {
completion(.finished)
return
}
Task { @MainActor in
LoggingService.shared.info("macOS background activity started", category: .notifications)
await self.backgroundRefresher.performBackgroundRefresh()
completion(.finished)
LoggingService.shared.info("macOS background activity completed", category: .notifications)
}
}
self.activityScheduler = scheduler
LoggingService.shared.info("Registered macOS background activity", category: .notifications)
}
func invalidateMacOSScheduler() {
activityScheduler?.invalidate()
activityScheduler = nil
LoggingService.shared.debug("Invalidated macOS background scheduler", category: .notifications)
}
func restartMacOSScheduler() {
invalidateMacOSScheduler()
registerMacOSBackgroundActivity()
}
#endif
// MARK: - Enable/Disable
func handleNotificationsEnabledChanged(_ enabled: Bool) {
#if os(iOS)
if enabled {
scheduleIOSBackgroundRefresh()
} else {
cancelIOSBackgroundRefresh()
}
#elseif os(macOS)
if enabled {
if activityScheduler == nil {
registerMacOSBackgroundActivity()
}
} else {
invalidateMacOSScheduler()
}
#endif
}
}