首页 > 解决方案 > 如何使用 BGTaskScheduler 在后台快速调用 API?

问题描述

在我的应用程序中,我们必须调用一项服务来刷新令牌。因此,出于这种想法,可以使用后台调度程序和调度任务来调用 API。

在那个计划任务中,我必须在后台进行静默呼叫,以提供新的令牌。因此,当用户打开应用程序时,不会遇到任何不便。

为此,我结合了后台调度程序(BGTaskScheduler),使用BGProcessingTask,进行API调用(使用URLSessionConfiguration.background(withIdentifier:)

  1. 点击项目名称—> 选择目标-> 转到签名和功能 —> 点击添加—> 背景模式

  2. 检查后台获取和后台处理

  3. 在 info.plist 中添加标识符 在此处输入图像描述

  4. 创建自定义类,命名为BGTaskHelper

    import UIKit
    import Foundation
    import BackgroundTasks
    
    
    let TASK_ID_REFRESH_TOKEN = "com.sample.refresh"
    let SESSION_ID_REFRESH_CALL = "com.sample.refreshCall"
    
    class BGTaskHelper: NSObject, URLSessionDelegate {    
        static let shared = BGTaskHelper()
    
    private lazy var urlSession: URLSession = {
        let config = URLSessionConfiguration.background(withIdentifier: SESSION_ID_REFRESH_CALL)
        config.isDiscretionary = true
        config.sessionSendsLaunchEvents = true
        return URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }()
    
    func requestWith(strURL: String, method: String, params: [String: Any]?, completion: @escaping((Data?, URLResponse?, Error?)) -> Void) {
    
        guard let url = URL(string: strURL) else { return }
        print("REQUEST URL : =============== ", strURL)
    
        var request = URLRequest(url: url)
        request.httpMethod = method
        request.addValue("application/json", forHTTPHeaderField: "content-type")
    
        if let postParams = params {
            print("REQUEST PARAMS : =============== ", postParams)
            guard let httpbody = try? JSONSerialization.data(withJSONObject: postParams, options: []) else { return }
            request.httpBody = httpbody
        }
    
        let task = urlSession.dataTask(with: request) { (data, response, error) in
    
            guard let response = response, let data = data else {
                completion((nil, nil, error))
                return
            }
    
            // ------- Added to just print response --------------------------------
            do {
                let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any]
                print("RESPONSE JSON : =============== ", json ?? "")
            } catch let error{
                print("RESPONSE PARSING ERROR : =============== ", error)
            }
            // -----------------------------------------------------------------------
    
            completion((data, response, error))
        }
        task.resume()
    }
    
    static func registerAutoRefreshToken() {
    
        if #available(iOS 13.0, *) {
            BGTaskScheduler.shared.register(forTaskWithIdentifier: TASK_ID_REFRESH_TOKEN, using: nil) { task in
                task.setTaskCompleted(success: true)
                BGTaskHelper.shared.handleTokenRefresh(task: task)  //as! BGAppRefreshTask
            }
        } else {
            // Fallback on earlier versions
        }
    }
    
    static func scheduleTokenRefresh() {
    
        guard UserDefaults.standard.bool(forKey: "IS_LOGIN") == true else { return }
    
        if #available(iOS 13.0, *) {
    
            BGTaskScheduler.shared.cancelAllTaskRequests()
    
            let request = BGProcessingTaskRequest(identifier: TASK_ID_REFRESH_TOKEN)
            // Fetch no earlier than 15 minutes from now
            request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
            request.requiresNetworkConnectivity = true
    
            do {
                try BGTaskScheduler.shared.submit(request)
            } catch {
                print("Could not schedule token refresh : ", error)
            }
        }
    }
    
    @available(iOS 13.0, *)
    func handleTokenRefresh(task: BGTask) { //BGAppRefreshTask
    
        // 1. Schedule a new refresh task
        BGTaskHelper.scheduleTokenRefresh()
    
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
    
        // 2. Create an operation that performs the main part of the background task
    
        let operation = BlockOperation()
        operation.addExecutionBlock {
            if UserDefaults.standard.bool(forKey: "IS_LOGIN") == true {
                let dict: [String: Any] = ["userID" : "xyzUser",
                                           "token" : "last_saved_token"]
                BGTaskHelper.shared.requestWith(strURL: "https://example.com/xyzUser/tokenrefresh", method: "POST", params: dict) { (data, response, error) in
                    task.setTaskCompleted(success: true)
                    // saving new token
                }
            }
        }
    
    
        // 3. Provide an expiration handler for the background task that cancels the operation
        task.expirationHandler = {
            operation.cancel()
        }
    
        // 4. Inform the system that the background task is complete when the operation completes
        operation.completionBlock = {
            task.setTaskCompleted(success: !operation.isCancelled)
        }
    
        // 5. Start the operation
        queue.addOperation(operation)
    }
    }
    
  5. AppDelegate, didFinishLaunchingWithOptions, 调用 :BGTaskHelper.registerAutoRefreshToken()

  6. SceneDelegate, sceneDidEnterBackground, 调用 :BGTaskHelper.scheduleTokenRefresh()

这就是我所做的。我仍然没有完全测试它。但我很困惑这是否会完美运行?

标签: swift5ios14xcode12bgtaskscheduler

解决方案


推荐阅读