diff --git a/kDrive/AppDelegate+BGAppRefresh.swift b/kDrive/AppDelegate+BGAppRefresh.swift
deleted file mode 100644
index d69094dd3..000000000
--- a/kDrive/AppDelegate+BGAppRefresh.swift
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- Infomaniak kDrive - iOS App
- Copyright (C) 2023 Infomaniak Network SA
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
- */
-
-import BackgroundTasks
-import CocoaLumberjackSwift
-import Foundation
-import InfomaniakDI
-import kDriveCore
-
-extension AppDelegate {
- /* To debug background tasks:
- Launch ->
- e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.infomaniak.background.refresh"]
- OR
- e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.infomaniak.background.long-refresh"]
-
- Force early termination ->
- e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.infomaniak.background.refresh"]
- OR
- e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.infomaniak.background.long-refresh"]
- */
-
- /// schedule background tasks
- func scheduleBackgroundRefresh() {
- Log.bgTaskScheduling("scheduleBackgroundRefresh")
- // List pictures + upload files (+pictures) / photoKit
- let backgroundRefreshRequest = BGAppRefreshTaskRequest(identifier: Constants.backgroundRefreshIdentifier)
- #if DEBUG
- // Required for debugging
- backgroundRefreshRequest.earliestBeginDate = Date()
- #else
- backgroundRefreshRequest.earliestBeginDate = Date(timeIntervalSinceNow: 30 * 60)
- #endif
-
- // Upload files (+pictures) / photokit
- let longBackgroundRefreshRequest = BGProcessingTaskRequest(identifier: Constants.longBackgroundRefreshIdentifier)
- #if DEBUG
- // Required for debugging
- longBackgroundRefreshRequest.earliestBeginDate = Date()
- #else
- longBackgroundRefreshRequest.earliestBeginDate = Date(timeIntervalSinceNow: 30 * 60)
- #endif
- longBackgroundRefreshRequest.requiresNetworkConnectivity = true
- longBackgroundRefreshRequest.requiresExternalPower = true
- do {
- try backgroundTaskScheduler.submit(backgroundRefreshRequest)
- Log.bgTaskScheduling("scheduled task: \(backgroundRefreshRequest)")
- try backgroundTaskScheduler.submit(longBackgroundRefreshRequest)
- Log.bgTaskScheduling("scheduled task: \(longBackgroundRefreshRequest)")
-
- } catch {
- Log.bgTaskScheduling("Error scheduling background task: \(error)", level: .error)
- }
- }
-
- /// Register BackgroundTasks in scheduler for later
- func registerBackgroundTasks() {
- Log.bgTaskScheduling("registerBackgroundTasks")
- var registered = backgroundTaskScheduler.register(
- forTaskWithIdentifier: Constants.backgroundRefreshIdentifier,
- using: nil
- ) { task in
- self.scheduleBackgroundRefresh()
- @InjectService var uploadQueue: UploadQueue
- task.expirationHandler = {
- Log.bgTaskScheduling("Task \(Constants.backgroundRefreshIdentifier) EXPIRED", level: .error)
- uploadQueue.suspendAllOperations()
- uploadQueue.rescheduleRunningOperations()
- task.setTaskCompleted(success: false)
- }
-
- self.handleBackgroundRefresh { _ in
- Log.bgTaskScheduling("Task \(Constants.backgroundRefreshIdentifier) completed with SUCCESS")
- task.setTaskCompleted(success: true)
- }
- }
- Log.bgTaskScheduling("Task \(Constants.backgroundRefreshIdentifier) registered ? \(registered)")
-
- registered = backgroundTaskScheduler.register(
- forTaskWithIdentifier: Constants.longBackgroundRefreshIdentifier,
- using: nil
- ) { task in
- self.scheduleBackgroundRefresh()
- @InjectService var uploadQueue: UploadQueue
- task.expirationHandler = {
- Log.bgTaskScheduling("Task \(Constants.longBackgroundRefreshIdentifier) EXPIRED", level: .error)
- uploadQueue.suspendAllOperations()
- uploadQueue.rescheduleRunningOperations()
- task.setTaskCompleted(success: false)
- }
-
- self.handleBackgroundRefresh { _ in
- Log.bgTaskScheduling("Task \(Constants.longBackgroundRefreshIdentifier) completed with SUCCESS")
- task.setTaskCompleted(success: true)
- }
- }
- Log.bgTaskScheduling("Task \(Constants.longBackgroundRefreshIdentifier) registered ? \(registered)")
- }
-
- func handleBackgroundRefresh(completion: @escaping (Bool) -> Void) {
- Log.bgTaskScheduling("handleBackgroundRefresh")
- // User installed the app but never logged in
- if accountManager.accounts.isEmpty {
- completion(false)
- return
- }
-
- Log.bgTaskScheduling("Enqueue new pictures")
- @InjectService var photoUploader: PhotoLibraryUploader
- photoUploader.scheduleNewPicturesForUpload()
-
- Log.bgTaskScheduling("Clean errors for all uploads")
- @InjectService var uploadQueue: UploadQueue
- uploadQueue.cleanNetworkAndLocalErrorsForAllOperations()
-
- Log.bgTaskScheduling("Reload operations in queue")
- uploadQueue.rebuildUploadQueueFromObjectsInRealm()
-
- Log.bgTaskScheduling("waitForCompletion")
- uploadQueue.waitForCompletion {
- completion(true)
- }
- }
-}
diff --git a/kDrive/AppDelegate.swift b/kDrive/AppDelegate.swift
index e31bb955f..0fcdcce52 100644
--- a/kDrive/AppDelegate.swift
+++ b/kDrive/AppDelegate.swift
@@ -49,9 +49,9 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, AccountManagerDeleg
@LazyInjectService var backgroundUploadSessionManager: BackgroundUploadSessionManager
@LazyInjectService var backgroundDownloadSessionManager: BackgroundDownloadSessionManager
@LazyInjectService var photoLibraryUploader: PhotoLibraryUploader
- @LazyInjectService var backgroundTaskScheduler: BGTaskScheduler
@LazyInjectService var notificationHelper: NotificationsHelpable
@LazyInjectService var accountManager: AccountManageable
+ @LazyInjectService var backgroundTasksService: BackgroundTasksServiceable
// MARK: - UIApplicationDelegate
@@ -71,7 +71,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, AccountManagerDeleg
SentryDebug.capture(error: error)
}
- registerBackgroundTasks()
+ backgroundTasksService.registerBackgroundTasks()
// In some cases the application can show the old Nextcloud import notification badge
UIApplication.shared.applicationIconBadgeNumber = 0
@@ -167,8 +167,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, AccountManagerDeleg
func applicationDidEnterBackground(_ application: UIApplication) {
Log.appDelegate("applicationDidEnterBackground")
+ backgroundTasksService.scheduleBackgroundRefresh()
- scheduleBackgroundRefresh()
if UserDefaults.shared.isAppLockEnabled,
!(window?.rootViewController?.isKind(of: LockedAppViewController.self) ?? false) {
lockHelper.setTime()
@@ -183,19 +183,6 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, AccountManagerDeleg
shortcutItemToProcess = shortcutItem
}
- func application(_ application: UIApplication,
- performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
- Log.appDelegate("application performFetchWithCompletionHandler")
-
- handleBackgroundRefresh { newData in
- if newData {
- completionHandler(.newData)
- } else {
- completionHandler(.noData)
- }
- }
- }
-
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
Log.appDelegate("application app open url\(url)")
diff --git a/kDriveCore/DI/FactoryService.swift b/kDriveCore/DI/FactoryService.swift
index f6465660c..9f46da234 100644
--- a/kDriveCore/DI/FactoryService.swift
+++ b/kDriveCore/DI/FactoryService.swift
@@ -133,6 +133,9 @@ public enum FactoryService {
},
Factory(type: PhotoLibrarySavable.self) { _, _ in
PhotoLibrarySaver()
+ },
+ Factory(type: BackgroundTasksServiceable.self) { _, _ in
+ BackgroundTasksService()
}
]
return services
diff --git a/kDriveCore/Services/BackgroundTasksService.swift b/kDriveCore/Services/BackgroundTasksService.swift
new file mode 100644
index 000000000..c0a00ed3b
--- /dev/null
+++ b/kDriveCore/Services/BackgroundTasksService.swift
@@ -0,0 +1,142 @@
+/*
+ Infomaniak kDrive - iOS App
+ Copyright (C) 2023 Infomaniak Network SA
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+
+import BackgroundTasks
+import CocoaLumberjackSwift
+import Foundation
+import InfomaniakDI
+import kDriveCore
+
+/* To debug background tasks:
+ Launch ->
+ e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.infomaniak.background.refresh"]
+ OR
+ e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.infomaniak.background.long-refresh"]
+
+ Force early termination ->
+ e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.infomaniak.background.refresh"]
+ OR
+ e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.infomaniak.background.long-refresh"]
+ */
+
+/// Service to ask the system to do some work in the background later.
+public protocol BackgroundTasksServiceable {
+ /// Ask the system to handle the app's background refresh
+ func registerBackgroundTasks()
+
+ /// Schedule next refresh with the system
+ func scheduleBackgroundRefresh()
+}
+
+struct BackgroundTasksService: BackgroundTasksServiceable {
+ @LazyInjectService private var scheduler: BGTaskScheduler
+ @LazyInjectService private var accountManager: AccountManageable
+ @LazyInjectService private var uploadQueue: UploadQueue
+ @LazyInjectService private var photoUploader: PhotoLibraryUploader
+
+ public init() {
+ // META: keep SonarCloud happy
+ }
+
+ public func registerBackgroundTasks() {
+ Log.backgroundTaskScheduling("registerBackgroundTasks")
+ registerBackgroundTask(identifier: Constants.backgroundRefreshIdentifier)
+ registerBackgroundTask(identifier: Constants.longBackgroundRefreshIdentifier)
+ }
+
+ public func buildBackgroundTask(_ task: BGTask, identifier: String) {
+ scheduleBackgroundRefresh()
+
+ handleBackgroundRefresh { _ in
+ Log.backgroundTaskScheduling("Task \(identifier) completed with SUCCESS")
+ task.setTaskCompleted(success: true)
+ }
+
+ task.expirationHandler = {
+ Log.backgroundTaskScheduling("Task \(identifier) EXPIRED", level: .error)
+ uploadQueue.suspendAllOperations()
+ uploadQueue.rescheduleRunningOperations()
+ task.setTaskCompleted(success: false)
+ }
+ }
+
+ func registerBackgroundTask(identifier: String) {
+ let registered = scheduler.register(
+ forTaskWithIdentifier: identifier,
+ using: nil
+ ) { task in
+ buildBackgroundTask(task, identifier: identifier)
+ }
+ Log.backgroundTaskScheduling("Task \(identifier) registered ? \(registered)")
+ }
+
+ func handleBackgroundRefresh(completion: @escaping (Bool) -> Void) {
+ Log.backgroundTaskScheduling("handleBackgroundRefresh")
+ // User installed the app but never logged in
+ if accountManager.accounts.isEmpty {
+ completion(false)
+ return
+ }
+
+ Log.backgroundTaskScheduling("Enqueue new pictures")
+ photoUploader.scheduleNewPicturesForUpload()
+
+ Log.backgroundTaskScheduling("Clean errors for all uploads")
+ uploadQueue.cleanNetworkAndLocalErrorsForAllOperations()
+
+ Log.backgroundTaskScheduling("Reload operations in queue")
+ uploadQueue.rebuildUploadQueueFromObjectsInRealm()
+
+ Log.backgroundTaskScheduling("waitForCompletion")
+ uploadQueue.waitForCompletion {
+ completion(true)
+ }
+ }
+
+ func scheduleBackgroundRefresh() {
+ Log.backgroundTaskScheduling("scheduleBackgroundRefresh")
+ // List pictures + upload files (+pictures) / photoKit
+ let backgroundRefreshRequest = BGAppRefreshTaskRequest(identifier: Constants.backgroundRefreshIdentifier)
+ #if DEBUG
+ // Required for debugging
+ backgroundRefreshRequest.earliestBeginDate = Date()
+ #else
+ backgroundRefreshRequest.earliestBeginDate = Date(timeIntervalSinceNow: 30 * 60)
+ #endif
+
+ // Upload files (+pictures) / photokit
+ let longBackgroundRefreshRequest = BGProcessingTaskRequest(identifier: Constants.longBackgroundRefreshIdentifier)
+ #if DEBUG
+ // Required for debugging
+ longBackgroundRefreshRequest.earliestBeginDate = Date()
+ #else
+ longBackgroundRefreshRequest.earliestBeginDate = Date(timeIntervalSinceNow: 30 * 60)
+ #endif
+ longBackgroundRefreshRequest.requiresNetworkConnectivity = true
+ longBackgroundRefreshRequest.requiresExternalPower = true
+ do {
+ try scheduler.submit(backgroundRefreshRequest)
+ Log.backgroundTaskScheduling("scheduled task: \(backgroundRefreshRequest)")
+ try scheduler.submit(longBackgroundRefreshRequest)
+ Log.backgroundTaskScheduling("scheduled task: \(longBackgroundRefreshRequest)")
+
+ } catch {
+ Log.backgroundTaskScheduling("Error scheduling background task: \(error)", level: .error)
+ }
+ }
+}
diff --git a/kDriveCore/Utils/AbstractLog+Category.swift b/kDriveCore/Utils/AbstractLog+Category.swift
index d8c1fe3c5..7cafd1bed 100644
--- a/kDriveCore/Utils/AbstractLog+Category.swift
+++ b/kDriveCore/Utils/AbstractLog+Category.swift
@@ -90,13 +90,13 @@ public enum Log {
///
/// In system console, visualize them with `subsystem:com.infomaniak.drive category:BGTaskScheduling`
///
- public static func bgTaskScheduling(_ message: @autoclosure () -> Any,
- level: AbstractLogLevel = .debug,
- context: Int = 0,
- file: StaticString = #file,
- function: StaticString = #function,
- line: UInt = #line,
- tag: Any? = nil) {
+ public static func backgroundTaskScheduling(_ message: @autoclosure () -> Any,
+ level: AbstractLogLevel = .debug,
+ context: Int = 0,
+ file: StaticString = #file,
+ function: StaticString = #function,
+ line: UInt = #line,
+ tag: Any? = nil) {
let category = "BGTaskScheduling"
defaultLogHandler(message(),
category: category,