Reputation: 158
I'm trying out iOS Background Task for the first time. Following the sample WWDC'19 code, I set up a simple app that sets a UserDefaults
key's value to 1 in the ViewController.viewDidLoad()
and set it to 2 in the background task. I have the value printed before entering background and upon returning to foreground. The value consistently prints as 1, even after letting the app sit in the background overnight. Is my background task not run? Or could it be that my UserDefaults
value is not synchronized between the foreground and background tasks, as discussed in several stackoverflow questions? Or am I doing something else wrong? Any pointer appreciated.
I have checked the "Background fetch" item in "Background Modes" under "Signing & Capabilities" and I have also added "com.example.BGTask.refresh" as "item 0" under "Permitted background task scheduler" in my Info.plist
. I'm running the app on device, tethered to Xcode.
iOS 13.5.1, Xcode 11.5
AppDelegate.swift:
import UIKit
import BackgroundTasks
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.BGTask.refresh", using: nil) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
return true
}
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.example.BGTask.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15*60)
do {
try BGTaskScheduler.shared.submit(request)
} catch {
print("Could not schedule app refresh: \(error)")
}
}
func handleAppRefresh(task: BGAppRefreshTask) {
scheduleAppRefresh()
task.expirationHandler = {
task.setTaskCompleted(success: false)
}
UserDefaults.standard.set(2, forKey: "bgtask")
task.setTaskCompleted(success: true)
}
// MARK: UISceneSession Lifecycle
// rest of templated AppDelegate.swift
SceneDelegate.swift:
Following this tutorial, I call scheduleAppRefresh()
in SceneDelegate
instead of AppDelegate
and have modified these two functions:
func sceneDidBecomeActive(_ scene: UIScene) {
print("didBecomeActive: ", UserDefaults.standard.integer(forKey: "bgtask"))
}
func sceneDidEnterBackground(_ scene: UIScene) {
print("didEnterBackground: ", UserDefaults.standard.integer(forKey: "bgtask"))
(UIApplication.shared.delegate as! AppDelegate).scheduleAppRefresh()
}
Finally, in ViewController.swift:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
UserDefaults.standard.set(1, forKey: "bgtask")
print("viewDidLoad: ", UserDefaults.standard.integer(forKey: "bgtask"))
}
}
This is what I get printed out on the Xcode console:
viewDidLoad: 1
didBecomeActive: 1
// tap the Home button
didEnterBackground: 1
// after a suitably long time, tap the app icon
didBecomeActive: 1
Why doesn't UserDefaults.shared.integer(forKey: "bgtask")
get incremented by the second didBecomeActive
?
Upvotes: 7
Views: 7581
Reputation: 158
Well, I got background processing to work. Xcode console output:
viewDidLoad: 1
didBecomeActive: 1
didEnterBackground: 1
didBecomeActive: 1
didEnterBackground: 1
// after about 15 minutes
didBecomeActive: 2
didEnterBackground: 2
// after staying in background overnight (~8 hours)
didBecomeActive: 19
didEnterBackground: 19
Instead of "Background fetch," I ticked "Background processing" in "Background Modes" under "Signing & Capabilities".
AppDelegate.swift:
import UIKit
import BackgroundTasks
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.BGTask.refresh", using: nil) { task in
self.handleAppRefresh(task: task as! BGProcessingTask)
}
return true
}
func scheduleAppRefresh() {
let request = BGProcessingTaskRequest(identifier: "com.example.BGTask.refresh")
request.requiresNetworkConnectivity = false
request.requiresExternalPower = false
do {
try BGTaskScheduler.shared.submit(request)
} catch {
print("Could not schedule app refresh: \(error)")
}
}
func handleAppRefresh(task: BGProcessingTask) {
scheduleAppRefresh()
task.expirationHandler = {
task.setTaskCompleted(success: false)
}
// increment instead of a fixed number
UserDefaults.standard.set(UserDefaults.standard.integer(forKey: "bgtask")+1, forKey: "bgtask")
task.setTaskCompleted(success: true)
}
// MARK: UISceneSession Lifecycle
// rest of templated AppDelegate.swift
And no other changes to the other files.
Now why does BGProcessing work while BGAppRefresh doesn't?
Upvotes: 2
Reputation: 217
after I watch wwdc2019
https://developer.apple.com/videos/play/wwdc2019/707/
The task will be executed only when the system decides to do so
You can check to this forum discussion link too :
https://forums.developer.apple.com/thread/124320
Upvotes: 2