Skip to content

Commit

Permalink
Merge pull request #1101 from Infomaniak/refactorBGSched
Browse files Browse the repository at this point in the history
chore: Refactor background scheduler
  • Loading branch information
JorisBodin authored Jan 23, 2024
2 parents dfbab73 + 3214534 commit 8d19cd9
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 162 deletions.
139 changes: 0 additions & 139 deletions kDrive/AppDelegate+BGAppRefresh.swift

This file was deleted.

19 changes: 3 additions & 16 deletions kDrive/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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)")

Expand Down
3 changes: 3 additions & 0 deletions kDriveCore/DI/FactoryService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ public enum FactoryService {
},
Factory(type: PhotoLibrarySavable.self) { _, _ in
PhotoLibrarySaver()
},
Factory(type: BackgroundTasksServiceable.self) { _, _ in
BackgroundTasksService()
}
]
return services
Expand Down
142 changes: 142 additions & 0 deletions kDriveCore/Services/BackgroundTasksService.swift
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

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)
}
}
}
14 changes: 7 additions & 7 deletions kDriveCore/Utils/AbstractLog+Category.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 8d19cd9

Please sign in to comment.