Files
native/plugins/ios-push/PushRenewalTask.swift
T

63 lines
2.4 KiB
Swift

import BackgroundTasks
import Foundation
import UIKit
/// The app's side of PushRenewal: every shared account's subscription is
/// renewed when the app comes back to the foreground (the JS refresh runs
/// only on a cold start), and from a background app refresh task that iOS
/// runs now and then while the app is not used at all.
///
/// The config plugin (plugins/ios-push/index.js) compiles this into the app,
/// calls register() from AppDelegate's didFinishLaunching (BGTaskScheduler
/// wants its handlers registered before launch ends), and lists the task id
/// in Info.plist (BGTaskSchedulerPermittedIdentifiers, UIBackgroundModes
/// fetch).
enum PushRenewalTask {
static let identifier = "org.bulwarkmail.mobile.push-renewal"
/// A subscription lasts seven days; asking twice a day leaves iOS room to
/// skip runs (it decides when, and whether, the task runs).
static let interval: TimeInterval = 12 * 60 * 60
private static var observers: [NSObjectProtocol] = []
static func register() {
let registered = BGTaskScheduler.shared.register(forTaskWithIdentifier: identifier, using: nil) { task in
guard let refresh = task as? BGAppRefreshTask else {
task.setTaskCompleted(success: false)
return
}
handle(refresh)
}
if !registered { pushLog.error("push renewal task not registered") }
let center = NotificationCenter.default
observers = [
center.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: nil) { _ in
// No token refresh here: the app's own code refreshes on foreground.
Task { await PushRenewal.renewAll(allowRefresh: false) }
},
center.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { _ in
schedule()
},
]
}
static func schedule() {
let request = BGAppRefreshTaskRequest(identifier: identifier)
request.earliestBeginDate = Date(timeIntervalSinceNow: interval)
do {
try BGTaskScheduler.shared.submit(request)
} catch {
// The simulator has no background refresh.
pushLog.error("push renewal task not scheduled: \(String(describing: error), privacy: .public)")
}
}
private static func handle(_ task: BGAppRefreshTask) {
schedule()
let work = Task {
await PushRenewal.renewAll(allowRefresh: true)
task.setTaskCompleted(success: !Task.isCancelled)
}
task.expirationHandler = { work.cancel() }
}
}