iOS 13 中的后台任务不会 运行 多次
Background task in iOS 13 does not run more than once
我正在使用新的 iOS13 后台任务框架,实现了 BGAppRefreshTask 类型。我不知道我做错了什么,因为我尝试了很多不同的方法,但后台任务从未执行过一次以上
我想定期将用户的位置更新到服务器,我启用了后台位置更新和后台获取功能。
我尝试了方法
performFetchWithCompletionHandler
但它在 iOS 13 中贬值了,我正在 iPhone 11 中进行测试,它永远不会工作,除非我 运行 从调试 > 模拟后台获取
除此之外,我还尝试了我在 WWDC 2019
中看到的这段代码
在这里
应用委托:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
LocationHelper.shared.locationManager = CLLocationManager()
if #available(iOS 13.0, *) {
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.testApp.updateLocation", using: nil) { task in
//This task is cast with processing request (BGProcessingTask)
self.handleLocationUpdateTask(task: task as! BGAppRefreshTask)
}
self.registerLocalNotification()
} else {
// Fallback on earlier versions
}
}
func applicationDidEnterBackground(_ application: UIApplication) {
if #available(iOS 13.0, *) {
cancelAllPendingBGTask()
scheduleLocationUpdate()
} else {
// Fallback on earlier versions
}
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// For iOS < 13
// fetch data from internet now
if #available(iOS 13.0, *) {
}
else {
LocationHelper.shared.locationManager.startMonitoringSignificantLocationChanges()
fetchSomeData(completion: { (bool) in
if bool {
LocationHelper.shared.locationManager.stopMonitoringSignificantLocationChanges()
completionHandler(.newData)
} else {
LocationHelper.shared.locationManager.stopMonitoringSignificantLocationChanges()
completionHandler(.failed)
}
})
}
}
func registerLocalNotification() {
let notificationCenter = UNUserNotificationCenter.current()
let options: UNAuthorizationOptions = [.alert, .sound, .badge]
notificationCenter.requestAuthorization(options: options) {
(didAllow, error) in
if !didAllow {
print("User has declined notifications")
}
}
}
//MARK: Register BackGround Tasks
@available(iOS 13.0, *)
func scheduleLocalNotification() {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.delegate = self
notificationCenter.getNotificationSettings { (settings) in
if settings.authorizationStatus == .authorized {
self.fireNotification()
}
}
}
@available(iOS 13.0, *)
func scheduleLocationUpdate() {
let request = BGAppRefreshTaskRequest(identifier: "com.testApp.updateLocation") // BGProcessingTaskRequest(identifier: "com.testApp.updateLocation")
request.earliestBeginDate = Date(timeIntervalSinceNow: 10) // fetch data after 10 secs.
//Note :: EarliestBeginDate should not be set to too far into the future.
do {
try BGTaskScheduler.shared.submit(request)
} catch {
print("Could not schedule image fetch: (error)")
}
}
@available(iOS 13.0, *)
func handleLocationUpdateTask(task: BGAppRefreshTask) {
scheduleLocationUpdate()
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
let lastOperation = queue.operations.last!
task.expirationHandler = {
queue.cancelAllOperations()
}
lastOperation.completionBlock = {
task.setTaskCompleted(success: !lastOperation.isCancelled)
}
queue.addOperation {
self.fetchSomeData { (bool) in
self.scheduleLocalNotification()
task.setTaskCompleted(success: bool)
}
}
}
@available(iOS 13.0, *)
func cancelAllPendingBGTask() {
BGTaskScheduler.shared.cancelAllTaskRequests()
}
func fireNotification() {
// Create Notification Content
let notificationContent = UNMutableNotificationContent()
// Configure Notification Content
notificationContent.title = Constants.Values.appName.rawValue
notificationContent.body = "The app just tried to run a background task on \(UserDefaults.standard.string(forKey: "LastBackgroundFetch") ?? "NEVER")"
notificationContent.badge = 1
if #available(iOS 12.0, *) {
notificationContent.sound = .defaultCriticalSound(withAudioVolume: .infinity)
} else {
// Fallback on earlier versions
}
// Add Trigger
let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 1.0, repeats: false)
// Create Notification Request
let notificationRequest = UNNotificationRequest(identifier: "local_notification", content: notificationContent, trigger: notificationTrigger)
UNUserNotificationCenter.current().delegate = self
// Add Request to User Notification Center
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
if let error = error {
print("Unable to Add Notification Request (\(error), \(error.localizedDescription))")
}
}
}
func fetchSomeData(completion: @escaping (Bool) -> () ) {
LocationHelper.shared.updateUser { (bool) in
let date = DateFormatter.sharedDateFormatter.string(from: Date())
UserDefaults.standard.set(date, forKey: "LastBackgroundFetch")
completion(bool)
}
}
在这段代码中,我能够根据苹果的文档测试后台模式,方法是暂停应用程序,然后在调试器中键入这段代码,然后恢复
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.FindFamily.updateLocation"]
即使在这之后,代码只 运行 一次然后停止,实际上它应该继续调用自己,因为 scheduleLocationUpdate()
在函数 handleLocationUpdateTask(task:)
中被调用,这将被调用每 10 秒后(大约)
我发现了问题
我正在使用后台进程来实现一些没有它就可以实现的事情
我在功能部分启用了后台位置更新,它开始在后台为我提供持续更新。
我正在使用新的 iOS13 后台任务框架,实现了 BGAppRefreshTask 类型。我不知道我做错了什么,因为我尝试了很多不同的方法,但后台任务从未执行过一次以上 我想定期将用户的位置更新到服务器,我启用了后台位置更新和后台获取功能。 我尝试了方法
performFetchWithCompletionHandler
但它在 iOS 13 中贬值了,我正在 iPhone 11 中进行测试,它永远不会工作,除非我 运行 从调试 > 模拟后台获取 除此之外,我还尝试了我在 WWDC 2019
中看到的这段代码在这里
应用委托:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
LocationHelper.shared.locationManager = CLLocationManager()
if #available(iOS 13.0, *) {
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.testApp.updateLocation", using: nil) { task in
//This task is cast with processing request (BGProcessingTask)
self.handleLocationUpdateTask(task: task as! BGAppRefreshTask)
}
self.registerLocalNotification()
} else {
// Fallback on earlier versions
}
}
func applicationDidEnterBackground(_ application: UIApplication) {
if #available(iOS 13.0, *) {
cancelAllPendingBGTask()
scheduleLocationUpdate()
} else {
// Fallback on earlier versions
}
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// For iOS < 13
// fetch data from internet now
if #available(iOS 13.0, *) {
}
else {
LocationHelper.shared.locationManager.startMonitoringSignificantLocationChanges()
fetchSomeData(completion: { (bool) in
if bool {
LocationHelper.shared.locationManager.stopMonitoringSignificantLocationChanges()
completionHandler(.newData)
} else {
LocationHelper.shared.locationManager.stopMonitoringSignificantLocationChanges()
completionHandler(.failed)
}
})
}
}
func registerLocalNotification() {
let notificationCenter = UNUserNotificationCenter.current()
let options: UNAuthorizationOptions = [.alert, .sound, .badge]
notificationCenter.requestAuthorization(options: options) {
(didAllow, error) in
if !didAllow {
print("User has declined notifications")
}
}
}
//MARK: Register BackGround Tasks
@available(iOS 13.0, *)
func scheduleLocalNotification() {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.delegate = self
notificationCenter.getNotificationSettings { (settings) in
if settings.authorizationStatus == .authorized {
self.fireNotification()
}
}
}
@available(iOS 13.0, *)
func scheduleLocationUpdate() {
let request = BGAppRefreshTaskRequest(identifier: "com.testApp.updateLocation") // BGProcessingTaskRequest(identifier: "com.testApp.updateLocation")
request.earliestBeginDate = Date(timeIntervalSinceNow: 10) // fetch data after 10 secs.
//Note :: EarliestBeginDate should not be set to too far into the future.
do {
try BGTaskScheduler.shared.submit(request)
} catch {
print("Could not schedule image fetch: (error)")
}
}
@available(iOS 13.0, *)
func handleLocationUpdateTask(task: BGAppRefreshTask) {
scheduleLocationUpdate()
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
let lastOperation = queue.operations.last!
task.expirationHandler = {
queue.cancelAllOperations()
}
lastOperation.completionBlock = {
task.setTaskCompleted(success: !lastOperation.isCancelled)
}
queue.addOperation {
self.fetchSomeData { (bool) in
self.scheduleLocalNotification()
task.setTaskCompleted(success: bool)
}
}
}
@available(iOS 13.0, *)
func cancelAllPendingBGTask() {
BGTaskScheduler.shared.cancelAllTaskRequests()
}
func fireNotification() {
// Create Notification Content
let notificationContent = UNMutableNotificationContent()
// Configure Notification Content
notificationContent.title = Constants.Values.appName.rawValue
notificationContent.body = "The app just tried to run a background task on \(UserDefaults.standard.string(forKey: "LastBackgroundFetch") ?? "NEVER")"
notificationContent.badge = 1
if #available(iOS 12.0, *) {
notificationContent.sound = .defaultCriticalSound(withAudioVolume: .infinity)
} else {
// Fallback on earlier versions
}
// Add Trigger
let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 1.0, repeats: false)
// Create Notification Request
let notificationRequest = UNNotificationRequest(identifier: "local_notification", content: notificationContent, trigger: notificationTrigger)
UNUserNotificationCenter.current().delegate = self
// Add Request to User Notification Center
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
if let error = error {
print("Unable to Add Notification Request (\(error), \(error.localizedDescription))")
}
}
}
func fetchSomeData(completion: @escaping (Bool) -> () ) {
LocationHelper.shared.updateUser { (bool) in
let date = DateFormatter.sharedDateFormatter.string(from: Date())
UserDefaults.standard.set(date, forKey: "LastBackgroundFetch")
completion(bool)
}
}
在这段代码中,我能够根据苹果的文档测试后台模式,方法是暂停应用程序,然后在调试器中键入这段代码,然后恢复
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.FindFamily.updateLocation"]
即使在这之后,代码只 运行 一次然后停止,实际上它应该继续调用自己,因为 scheduleLocationUpdate()
在函数 handleLocationUpdateTask(task:)
中被调用,这将被调用每 10 秒后(大约)
我发现了问题 我正在使用后台进程来实现一些没有它就可以实现的事情 我在功能部分启用了后台位置更新,它开始在后台为我提供持续更新。