SwiftUI 上的 BackgroundTasks 和 BGTaskScheduler 未触发

Blu*_*rXD 6 ios swift background-task swiftui

我正在尝试设置一些BackgroundTasks在应用程序关闭时定期运行一些代码(比如每天一次)。我相信BackgroundTasksswift 上的 API 就是为了这个?(如果我弄错了,请告诉我)。我按照Apple 文档上的文章来实现它,并调整它以适应 SwiftUI。

问题:后台任务永远不会触发,但处于“待处理”状态(如图所示)

免责声明:我确实添加了该Background Modes功能并进行了检查Background fetchBackground processing并将标识符添加到Info.plist

代码

Main- 设置应用程序委托,获取场景,设置 UserDefaults 计数器,获取所有待处理任务的按钮,显示 UserDefault 计数器的文本,添加到 UserDefault 计数器的按钮,schedule()当应用程序进入后台时调用任务

import SwiftUI
import BackgroundTasks

@main
struct GyfterApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @Environment(\.scenePhase) var scene
    @AppStorage("test") var test = 1
    
    var body: some Scene {
        WindowGroup {
            VStack {
                Button("Test") {
                    BGTaskScheduler.shared.getPendingTaskRequests { all in
                        print("Pending Tasks Requests", all)
                    }
                }
                Text("\(test)")
                Button("ADD") {
                    test = test + 1
                }
            }
                .onChange(of: scene) { newValue in
                    switch newValue {
                    case .background:
                        print("Entered Background")
                        appDelegate.schedule()
                    default:
                        break
                    }
                }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Operation- 操作BackgroundTasks,它打印一条消息并向 UserDefaults 计数器加 1

class OP: Operation {
    @AppStorage("test") var test = 1
    
    override func main() {
        print("OPERATION RAN")
        test = test + 1
    }
}
Run Code Online (Sandbox Code Playgroud)

AppDelegate-

  • 使用给定的标识符注册任务,在运行时打印消息,并打印注册调用的输出以查看它是否已注册(它已注册但未调用handleSchedule(task:)
  • 使用标识符安排任务请求,提前 60 秒,提交给调度程序(schedule()仅在应用程序进入后台时调用,而不是在handleSchedule(task:)应该触发时永远不会调用)
  • handleSchedule(task:) 打印语句、调用schedule()、创建OperationQueue、创建Operation、设置expirationHandlersetTaskCompleted添加操作到队列
class AppDelegate: NSObject, UIApplicationDelegate {
    
    @AppStorage("test") var test = 1
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        print("LAUNCHED")
        let a = BGTaskScheduler.shared.register(forTaskWithIdentifier: "IDENTIFIER", using: nil) { task in
            print("REGISTERED")
            self.test = self.test + 1
            self.handleSchedule(task: task as! BGAppRefreshTask)
        }
        print(a)
        return true
    }
    
     func schedule() {
        let request = BGAppRefreshTaskRequest(identifier: "IDENTIFIER")
        request.earliestBeginDate = Date(timeIntervalSinceNow: 60)
        
       do {
          try BGTaskScheduler.shared.submit(request)
       } catch {
          print("Could not schedule app refresh: \(error)")
       }
    }
    
     func handleSchedule(task: BGAppRefreshTask) {
        print("HANDLING SCHEDULE")
        schedule()
        
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        
        let operation = OP()

        
        task.expirationHandler = {
            print("BG Task Expired")
            queue.cancelAllOperations()
        }
        
        operation.completionBlock = {
            print("OPERATION COMPLETED")
            task.setTaskCompleted(success: !operation.isCancelled)
        }
        
        queue.addOperation(operation)
    }
}

Run Code Online (Sandbox Code Playgroud)

控制台日志:显示函数调用的打印语句

  • 第 1 行:应用程序启动
  • 第 2 行:(BGTaskScheduler据称)使用标识符“注册”任务
  • 第 3 行:待处理任务的数组
  • 第 4 行:应用程序进入后台(并被schedule()调用)
  • 第 5 行:待处理任务的数组
LAUNCHED
true
Pending Tasks Requests []
Entered Background
Pending Tasks Requests [<BGAppRefreshTaskRequest: INDENTIFIER, earliestBeginDate: 2022-02-28 02:57:50 +0000>]
Run Code Online (Sandbox Code Playgroud)

Bri*_*ock 0

我将首先通过强制后台任务在设备上运行来调试它,以便您知道它是否成功: e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"IDENTIFIER"]

然而,我的预感是,当您离开范围时,您的操作队列只是被释放,因为它没有连接到任何东西。可能您需要waitUntilAllOperationsAreFinished()确保此代码不会通过排队操作退出作用域。