BGAppRefreshTask 后台任务未执行

Dea*_*rae 9 background task ios swift

我正在使用新的 iOS13 后台任务框架,并实现了 BGAppRefreshTask 类型。我的问题是,我的设备从不调用任务,即使等待了几个小时,但我能够使用调用 _simulateLaunchForTaskWithIdentifier 的调试器技巧成功运行代码。

我已经设置了以下内容:

  • 使用后台模式功能启用我的应用程序,检查“后台获取”。
  • 在“允许的后台任务调度程序标识符”下将我的背景 ID 添加到 Info.plist:“com.XYZ.PearWeather.backgroundAlerts”。

我已经在我的 AppDelegate 中从 application(didFinishLaunchingWithOptions) 注册了任务:

        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.XYZ.PearWeather.backgroundAlerts", using: nil) { task in
            self.backgroundAlerts(task: task as! BGAppRefreshTask)
        }
Run Code Online (Sandbox Code Playgroud)

我正在 AppDelegate 内的 func 中调度任务,并从我的 SceneDelegate sceneDidEnterBackground() 调用它。它原本是一个静态的func,但我现在已经把它改成了一个实例func,并得到了AppDelegate实例(因为我在绝望中尝试了很多变化):

    func sceneDidEnterBackground(_ scene: UIScene) {
        (UIApplication.shared.delegate as! AppDelegate).scheduleBackgroundAlerts()
    }
Run Code Online (Sandbox Code Playgroud)
    func scheduleBackgroundAlerts() {
        let request = BGAppRefreshTaskRequest(identifier: "com.XYZ.PearWeather.backgroundAlerts")

        request.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60)

        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            print("Could not schedule app refresh: \(error)")
        }
    }
Run Code Online (Sandbox Code Playgroud)

至少在Debugger场景下,submit调用是没有错误的。我为上面的 timeIntervalSinceNow 参数尝试了许多不同的值。我还从任务处理程序本身调用了这个 scheduleBackgroundAlerts() 函数,如下所示:

    func backgroundAlerts(task: BGAppRefreshTask) {

        scheduleBackgroundAlerts()

        task.expirationHandler = {
            // After all operations are cancelled, the completion block below is called to set the task to complete.
            task.setTaskCompleted(success: false)
        }

        AlertsOperation.showNotification()


        task.setTaskCompleted(success: true)
    }

Run Code Online (Sandbox Code Playgroud)

这个实现改变了很多 - 我最初使用了一个 OperationQueue,尝试在 func 的开头和结尾放置 scheduleBackgroundAlerts() 调用,等等。现在它被剥离了。AlertOperation.showNotification() 现在也非常简单:

    static func showNotification() {

        let now = Date()
        let bg = Locale.currentLocale().formattedTime(date: now)
        SettingsManager.shared.settings.bg = bg
    }
Run Code Online (Sandbox Code Playgroud)

这只是在 UserDefaults 中存储一个值(在我的 SettingsManager 中,其详细信息与此处无关),我可以在我的应用程序中读回该值以查看是否发生了任何事情。

现在,此 func 的原始实现使用 UNUserNotificationCenter 等发出本地通知,这就是我在此后台任务中尝试执行的操作。这在调试器中运行良好,但我将其简化为这个简单的代码,只是为了实现一个非常小的实现。

正如我所说,从调试器调用任务处理程序工作正常,使用:

e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.XYZ.PearWeather.backgroundAlerts"]
Run Code Online (Sandbox Code Playgroud)

但是设备本身没有发生任何事情。我看不到我错过了什么。我也不知道如何从后台任务处理程序中记录任何异常。

我是 Swift 和 iOS 的新手,因此感谢您的指点。上面的大部分代码几乎是关于这个主题的许多教程的副本。然而,对我来说,事情并没有奏效,我已经没有选择了!

Sre*_*thI 5

request(BGTaskScheduler.shared.submit(request))提交请求后和提交后运行命令后代码中有断点

e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.XYZ.PearWeather.backgroundAlerts"]
Run Code Online (Sandbox Code Playgroud)

然后将为该标识符调用注册的处理程序。检查POC以了解更多详细信息。


Way*_*son 1

这是我使用的。

在我的 AppDelegate 中,iOS13 代码。不包括旧式获取:

class AppDelegate: UIResponder, UIApplicationDelegate, URLSessionDelegate, UNUserNotificationCenterDelegate {
var backgroundSessionCompletionHandler: (() -> Void)?
var backgroundSynchTask: UIBackgroundTaskIdentifier = .invalid

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13.0, *) {
    BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.WH.Air-Compare.WayneBGrefresh", using: nil) { task in
        self.handleAppRefresh(task: task as! BGAppRefreshTask)
        self.logSB.verbose("iOS 13  Registered for Background duty")
    }
} else {
    // Fallback on earlier versions
    self.logSB.verbose("iOS < 13  Registering for Background duty")
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
}
}

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler:@escaping () -> Void) {
let dateFormatter = DateFormatter()  // format the date for output
dateFormatter.dateStyle = DateFormatter.Style.medium
dateFormatter.timeStyle = DateFormatter.Style.long
let convertedDate = dateFormatter.string(from: Date())
self.logSB.info("Background URLSession handled at \(convertedDate)")
self.logSB.info("Background URLSession ID \(identifier)")
let config = URLSessionConfiguration.background(withIdentifier: "WayneBGconfig")
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
session.getTasksWithCompletionHandler { (dataTasks, uploadTasks, downloadTasks) -> Void in
    // yay! you have your tasks!
    self.logSB.info("Background completion handler here with \(downloadTasks.count) tasks")
    for i in 0...max(0,downloadTasks.count - 1) {
        let description: String = downloadTasks[i].taskDescription!
        self.logSB.info("Task ID \(i) is \(description)")
    }
}
backgroundSessionCompletionHandler = completionHandler
}

func applicationDidEnterBackground(_ application: UIApplication) {
if #available(iOS 13.0, *) {
    scheduleAppRefresh()
} else {
    // Fallback on earlier versions
}
}

@available(iOS 13.0, *)
func scheduleAppRefresh() {
logSB.info("Scheduling the AppRefresh in iOS 13 !!")
let request = BGAppRefreshTaskRequest(identifier: "com.WH.Air-Compare.WayneBGrefresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 2 * 60) // Fetch no earlier than 2 minutes from now

do {
    try BGTaskScheduler.shared.submit(request)
} catch {
    logSB.error("Could not schedule app refresh: \(error)")
}
}
Run Code Online (Sandbox Code Playgroud)