Add a search backfill app background processing task

To run when the device is idle, charging and connected to the network.
This commit is contained in:
Stefan Ceriu
2026-09-28 18:57:10 +03:00
parent d08b0e0090
commit 49481f9af7
4 changed files with 67 additions and 1 deletions
@@ -170,6 +170,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
observeAppLockChanges()
registerBackgroundAppRefresh()
registerSearchBackfill()
appSettings.analyticsConsentStatePublisher
.dropFirst() // Sentry is configured during init; only reconfigure when consent state actually changes
@@ -1189,6 +1190,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
scheduleDelayedPauseServices()
scheduleBackgroundAppRefresh()
scheduleSearchBackfill()
}
@objc
@@ -1325,3 +1327,63 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
}
}
}
// MARK: Search backfill
private extension AppCoordinator {
func registerSearchBackfill() {
let result = BGTaskScheduler.shared.register(forTaskWithIdentifier: appSettings.searchBackfillTaskIdentifier, using: .main) { [weak self] task in
guard let task = task as? BGProcessingTask else {
MXLog.error("Invalid search backfill configuration")
return
}
Task {
await self?.handleSearchBackfill(task)
}
}
MXLog.info("Register search backfill with result: \(result)")
}
func scheduleSearchBackfill() {
guard let userSession, userSession.userSettings.globalSearchEnabled, #available(iOS 26.0, *) else {
return
}
let request = BGProcessingTaskRequest(identifier: appSettings.searchBackfillTaskIdentifier)
request.requiresExternalPower = true
request.requiresNetworkConnectivity = true
do {
try BGTaskScheduler.shared.submit(request)
MXLog.info("Successfully scheduled search backfill task")
} catch {
MXLog.error("Failed scheduling search backfill with error: \(error)")
}
}
func handleSearchBackfill(_ task: BGProcessingTask) async {
MXLog.info("Started search backfill task")
guard let clientProxy = userSession?.clientProxy else {
task.setTaskCompleted(success: false)
return
}
task.expirationHandler = { @Sendable [weak self] in
MXLog.info("Search backfill task is about to expire.")
Task { @MainActor in self?.userSession?.clientProxy.stopSearchBackfill() }
}
clientProxy.startSearchBackfill(strategy: .background)
// TaskHandle can't be awaited, so poll until the sweep finishes or is stopped on expiry.
while clientProxy.isSearchBackfillRunning {
try? await Task.sleep(for: .seconds(1))
}
MXLog.info("Marking search backfill task as complete.")
task.setTaskCompleted(success: true)
}
}
@@ -151,6 +151,8 @@ final nonisolated class AppSettings: @unchecked Sendable {
/// The task identifier used for background app refresh. Also used in main target's the Info.plist
let backgroundAppRefreshTaskIdentifier = "io.element.elementx.background.refresh"
/// The task identifier used for the search index backfill. Also used in main target's the Info.plist
let searchBackfillTaskIdentifier = "io.element.elementx.background.search-backfill"
/// A URL where users can go read more about the app.
private(set) var websiteURL: URL = "https://element.io"
+1
View File
@@ -5,6 +5,7 @@
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>io.element.elementx.background.refresh</string>
<string>io.element.elementx.background.search-backfill</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
+2 -1
View File
@@ -100,7 +100,8 @@ targets:
location
]
BGTaskSchedulerPermittedIdentifiers: [
io.element.elementx.background.refresh
io.element.elementx.background.refresh,
io.element.elementx.background.search-backfill
]
UTExportedTypeDeclarations:
- UTTypeConformsTo: [public.text]