// // BGTask.swift // Reflect (iOS) // // Created by Trey Tartt on 1/12/22. // import Foundation import BackgroundTasks class BGTask { static let updateDBMissingID = "com.88oakapps.reflect.dbUpdateMissing" static let weatherRetryID = "com.88oakapps.reflect.weatherRetry" static let weeklyDigestID = "com.88oakapps.reflect.weeklyDigest" @MainActor class func runFillInMissingDatesTask(task: BGProcessingTask) { BGTask.scheduleBackgroundProcessing() task.expirationHandler = { task.setTaskCompleted(success: false) } DataController.shared.fillInMissingDates() // Catch up on any side effects from widget/watch votes MoodLogger.shared.processPendingSideEffects() task.setTaskCompleted(success: true) } @MainActor class func runWeatherRetryTask(task: BGProcessingTask) { BGTask.scheduleWeatherRetry() task.expirationHandler = { task.setTaskCompleted(success: false) } Task { await WeatherManager.shared.retryPendingWeatherFetches() task.setTaskCompleted(success: true) } } class func scheduleWeatherRetry() { let request = BGProcessingTaskRequest(identifier: BGTask.weatherRetryID) request.requiresNetworkConnectivity = true request.requiresExternalPower = false request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) do { try BGTaskScheduler.shared.submit(request) } catch { print("Could not schedule weather retry: \(error)") } } @MainActor class func runWeeklyDigestTask(task: BGProcessingTask) { BGTask.scheduleWeeklyDigest() task.expirationHandler = { task.setTaskCompleted(success: false) } if #available(iOS 26, *) { Task { guard !IAPManager.shared.shouldShowPaywall else { task.setTaskCompleted(success: true) return } do { let digest = try await FoundationModelsDigestService.shared.generateWeeklyDigest() // Send local notification with the headline let personalityPack = UserDefaultsStore.personalityPackable() LocalNotification.scheduleDigestNotification(headline: digest.headline, personalityPack: personalityPack) task.setTaskCompleted(success: true) } catch { print("Weekly digest generation failed: \(error)") task.setTaskCompleted(success: false) } } } else { task.setTaskCompleted(success: true) } } class func scheduleWeeklyDigest() { let request = BGProcessingTaskRequest(identifier: BGTask.weeklyDigestID) request.requiresNetworkConnectivity = false request.requiresExternalPower = false // Schedule for next Sunday at 7 PM let calendar = Calendar.current var components = calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: Date()) components.weekday = 1 // Sunday components.hour = 19 components.minute = 0 var nextSunday = calendar.date(from: components) ?? Date() if nextSunday <= Date() { nextSunday = calendar.date(byAdding: .weekOfYear, value: 1, to: nextSunday)! } request.earliestBeginDate = nextSunday do { try BGTaskScheduler.shared.submit(request) } catch { print("Could not schedule weekly digest: \(error)") } } class func scheduleBackgroundProcessing() { let request = BGProcessingTaskRequest(identifier: BGTask.updateDBMissingID) request.requiresNetworkConnectivity = false request.requiresExternalPower = false var runDate = Calendar.current.date(byAdding: .day, value: 1, to: Date()) runDate = Calendar.current.date(bySettingHour: 0, minute: 1, second: 0, of: runDate!) request.earliestBeginDate = runDate do { try BGTaskScheduler.shared.submit(request) } catch { print("Could not schedule image fetch: \(error)") } } }