Post

Replies

Boosts

Views

Activity

Reply to Automatic Background File Uploads
Thank you for this! I have a very similar use case where we need to upload heath data nightly. I just changed our implementation to use BGProcessingTaskRequest, however it still didn't seem to work. We use SwiftUI. Please note that the following code is written for testing, so it is scheduled to be 1 minute out. And since I am running this often, I have the App Delegate cancel previous tasks before registering/scheduling a new one. In real life, we will have it scheduled for 3 AM daily, and the app delegate will check if a task is already scheduled and, if not, schedule it. Also note that we have user consent and permissions enabled and this process is handled elsewhere. In our AppDelegate class, we have: func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { self.healthKitStuff() return true } func healthKitStuff() { BGTaskScheduler.shared.getPendingTaskRequests { result in let alreadyScheduled = result.contains { $0.identifier == Tasks.healthKitDataSync.rawValue } if !alreadyScheduled { HealthKit.shared.registerHealthKitSyncJob() HealthKit.shared.scheduleHealthKitSyncJob() } else { HealthKit.shared.cancelHealthKitSyncJob() self.healthKitStuff() } } } In our HealthKit.swift file: import Foundation import BackgroundTasks import HealthKit import SwiftUI class HealthKit: NSObject { static let shared = HealthKit() // identifier defined in info.plist -> 'Permitted background task scheduler identifiers' private var isSupported: Bool { return HKHealthStore.isHealthDataAvailable() } } // Sync HealthKit data with server extension HealthKit { func uploadYesterdaysHealthData(completionHandler: @escaping () -> Void) { print("xxx7 in uploadYesterdaysHealthData") fetchYesterdaysHealthData() { input in print("input is : \(input)") let mutation = SaveFitnessDataMutation(input: input) Api.shared.apollo.perform(mutation: mutation) { result in completionHandler() switch result { case .success(let data): if let resultData = data.data { print("START printing result data") print(resultData.saveUserFitness.success) print(resultData.saveUserFitness.message) print("END printing result data") } if let errors = data.errors { print("Error! : \(errors)") } case .failure(let error): print("Error! : \(error)") } } } } func registerHealthKitSyncJob() { print("xxx4 in registerHealthKitSyncJob") BGTaskScheduler.shared.register(forTaskWithIdentifier: Tasks.healthKitDataSync.rawValue, using: nil) { task in print("xxx5 task is: \(task)") HealthKit.shared.uploadYesterdaysHealthData() { print("xxx6 task completed") task.setTaskCompleted(success: true) // call the job again to run the next day print("calling scheduleHealthKitSyncJob from register") self.scheduleHealthKitSyncJob() } } } func cancelHealthKitSyncJob() { print("CALLING CANCEL") BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: Tasks.healthKitDataSync.rawValue) } func scheduleHealthKitSyncJob() { print("zzz3 in scheduleHealthKitSyncJob") //let request = BGAppRefreshTaskRequest(identifier: Tasks.healthKitDataSync.rawValue) let request = BGProcessingTaskRequest(identifier: Tasks.healthKitDataSync.rawValue) print("request is \(request)") var runAt: Date { let calendar = Calendar.current let startOfDay = calendar.startOfDay(for: Date()) let tomorrow = calendar.date(byAdding: .day, value: 1, to: startOfDay) let runAt = Date().adding(minutes: 1) //calendar.date(byAdding: .hour, value: 3, to: tomorrow!) // Scheduling job to run day after user consent since we look at previous day's data print("runAt is: \(String(describing: runAt))") return runAt } request.earliestBeginDate = runAt do { try BGTaskScheduler.shared.submit(request) print("submitted task request") } catch { print("Could not schedule HealthKit Sync Job: \(error)") } print("zzz3 END scheduleHealthKitSyncJob") } } extension Date { func adding(minutes: Int) -> Date { return Calendar.current.date(byAdding: .minute, value: minutes, to: self)! } } Thank you!
Feb ’25